简化题意
将 (w_i) 全部减 (1),则问题变成一个序列,其中给定有 (n) 个正数 (w_i),其余都是 (-1),和是 (0),前缀和均非负,求排列方案数。
做法
如果和是 (1),且要求前缀和全为正数,有结论:每个排列的所有循环移位只有 (1) 种合法,所以计数圆排列数即可,《具体数学》7.5 章有详细证明,其实就是每次必须要取最后一个最小值位置作为起点,使得其他最小值位置跨过序列结尾,前缀和为 (1),否则如果有两个相等最小值而取前一个作为起点,第二个最小值位置前缀和是 (0)。
本题前缀和可以为 (0),因此每个最小值都能取,分析失效。
证 Catalan 数时,可以在开头放个 (1),但本题中有多种不等价的正数,这种做法是无效的。
在结尾放个 (-1),问题变成前 (m) 个元素前缀和非负,且总和为 (-1)。
类似地,所有循环移位中现在只能取第一个最小值位置作为起点了,否则跨过序列结尾,之前的最小值位置前缀和会变成 (-1)。
(m+1) 个点共有 (m!) 种圆排列,而序列中有 (m - n + 1) 个等价的 (-1) 只能取额外放置的 (1) 个放在结尾(结尾一定是 (-1)),因此答案 (displaystylefrac{m!}{m - n + 1})。
P.S. 对于第一个结论,如果和为 (s),那么有 (s) 种循环移位。
#include <cstdio>
int main() {
int n, m = 0, s = 1, x;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &x);
m += x;
}
for (int i = 2, li = m - n; i <= li; ++i)
s = 1LL * s * i % 998244353;
for (int i = m - n + 2; i <= m; ++i)
s = 1LL * s * i % 998244353;
printf("%d
", s);
return 0;
}