- 共有(m)张牌,其中(n)张牌上写有数字(w_i),满足(sum w_i=m)。
- 一开始你先摸走牌库最顶上的一张牌,每当你摸到一张写着数字(x)的牌,你就再摸(x)张牌。
- 问有多少种牌库,你能把所有牌摸完。
- (nle40,w_ile10^5)
(Raney)引理
做这道题必需的一个引理:
一个环(x_1,x_2,...,x_n)满足(sum x_i=1),在将它裂开形成的(n)条可能的链中,恰有一条满足所有前缀和都是正数。
懒得另开一篇博客,就在这里顺便讲一下用(Raney)引理证明卡特兰数(C_n=frac{(2n)!}{n!(n+1)!})。
因为共有(n)个(1)和(n)个(-1),它们的和不为(1),因此我们强行在序列开头加上一个(1),这样就满足所有数和为(1)。
由于所有前缀和为正数,因此第一个数肯定是(1)而不是(-1),那么去掉开头就变成了剩余所有前缀和非负,恰好满足卡特兰数的限制条件。
这样就能推导出卡特兰数的通项公式(C_n=frac{(2n)!}{n!(n+1)!})。
关于此题的解法
首先我们把所有数都减去(1),此时所有数和为(0),且需要满足所有前缀都是非负数。
一个(naive)的想法是和前面卡特兰数的推导一样,强行在序列开头加上一个(1),满足所有数和为(1),且变成所有前缀都是正数。
但是,毕竟还是不一样,因为卡特兰数中正数只有(1),开头必须是(1),而这道题则不一定,所以会计算出错。
于是我们必须换个思路,发现有一个东西是唯一的,就是负数只有(-1)!
所以我们改成在序列末尾加上一个(-1),那么序列总和是(-1),这样一来只要将所有数都取反并逆序,就转化成了(Raney)引理的标准模型,且可以保证计算不重复(因为取反后的正数就只有(1)了)。
此时共有(m+1)个数,因此圆排列数量应该是((m+1-1)!=m!)。
然后为了除掉这个(-1)的影响,考虑原本有(m-n)个(-1),现在添上一个(-1)就有(m-n+1)个(-1),为了消除贡献还需要将(m!)除去(m-n+1)。
因此答案就是:
因为(m-n+1le m),所以只要在求阶乘的时候忽略其影响即可。
代码:(O(sum w_i))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 40
#define X 998244353
using namespace std;
int n,m;
int main()
{
RI i,t;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&t),m+=t;//m为所有数的总和
for(t=i=1;i<=m;++i) i^(m-n+1)&&(t=1LL*t*i%X);return printf("%d
",t),0;//计算答案,忽略m-n+1
}