把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷6672】[清华集训2016] 你的生命已如风中残烛(Raney引理)

点此看题面

  • 共有\(m\)张牌,其中\(n\)张牌上写有数字\(w_i\),满足\(\sum w_i=m\)
  • 一开始你先摸走牌库最顶上的一张牌,每当你摸到一张写着数字\(x\)的牌,你就再摸\(x\)张牌。
  • 问有多少种牌库,你能把所有牌摸完。
  • \(n\le40,w_i\le10^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\)

因此答案就是:

\[\frac{m!}{m-n+1} \]

因为\(m-n+1\le 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\n",t),0;//计算答案,忽略m-n+1
}
posted @ 2021-03-29 16:02  TheLostWeak  阅读(128)  评论(0编辑  收藏  举报