题解 P6672 【[清华集训2016] 你的生命已如风中残烛】
感觉有些东西题解讲的迷啊,而且都比较简略……希望这篇题解能把这道神题所有点讲清楚qwq
Step 1 问题转化
如果我们把每个数都减去 1 (
Step 2 Raney 引理
读过《具体数学》的会知道有这样一个引理(Page 301)
如果
x1,x2,...,x_m 是任何一个和为 1 的整数数列,则其所有循环位移中恰好有一个满足所有的前缀和都是正数。
这个是 Raney 引理。证明方法也比较简单,此处不另行证明。
我们还可以让它看上去简单一些(方便计数)
对于一个环
x1,x2,...x_m 满足\sum x=1 ,则破环为链后形成的n 种可能的链,只有一个链满足所有前缀和为正数。
所以,假设
Step 3 返回原题
原题中说的是
《具体数学》中有一个例子,有多少由
那这道题能不能用相同的方法呢?答案是不能。比如这个例子
考虑两个合法方案
我们知道了我们不能拆成
第一种解释就是其他几篇题解的解释。
第二种解释为,我们在末尾加上 -1 后,让所有数(包括这个-1)取反,然后再逆序一下,就有:有多少排列满足所有前缀和都为正数,且最前端为
然后我们有排列的数量=圆排列的数量=
最后除掉我们这个加上的
所以我们有
希望本题解能让你对 Raney 引理以及组合计数有更深的理解!
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int mod=998244353;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
signed main() {
int n=read(),m=0,ans=1;
rep(i,1,n) m+=read();
rep(i,2,m) ans=ans*(i==m-n+1?1:i)%mod;
printf("%lld\n",ans);
return 0;
}