【洛谷P6672】你的生命已如风中残烛

题目

题目链接:https://www.luogu.com.cn/problem/P6672
期中考试考完了,已经感到没有什么好害怕的六花今天决定不学数学了,于是和勇太打起了游戏王。

“你已空手空场,生命只剩一百,事到如今你还能做什么?”

“所累哇多卡纳!”

“纳尼?”

然而六花的卡组实在是太菜了,经过分析,六花发现在一回合内,她卡组中的牌并没有办法达成 OTK,除非主角光环附体:

被封印的艾克佐迪亚

——包括这张卡在内,「被封印者的右足」「被封印者的左足」「被封印者的右腕」「被封印者的左腕」全在手牌的时候,获得决斗胜利。

但是因为六花不是高贵的氪金玩家,她可以肯定,这五张牌中肯定有一张,在牌堆的底端。所以六花现在面临着一个难题:需要在一回合内将卡组抽完。

六花的牌堆一共有 \(m+1\) 张牌,因为最后一张牌是固定的,所以我们现在只考虑前 \(m\) 张牌。

在这 \(m\) 张牌中,有 \(n\) 张特殊牌和 \(m−n\) 张普通牌,每一个特殊牌有一个属性值 \(w_i\),表示在打出这张牌后,可以再摸 \(w_i\) 张牌。幸运的是,六花发现这些牌刚好满足 \(∑\limits_{i=1}^nw_i=m\),因此她可以放心的随意摸牌而不用担心爆牌。

因为这 \(m\) 张牌是被打乱的,所以总共有 \(m!\) 种不同的可能的牌堆。

现在这回合开始了,六花先从牌堆里抽出一张牌,接着六花不断的打出手中的牌,如果打出特殊牌,则又可以摸牌,直到摸到最后一张牌达成胜利条件或者打光自己的手牌结束自己的回合继而输掉比赛。

举例来说,如果牌堆是 \(\{4,0,0,2,0,0,0\}\)(用 \(0\) 表示普通牌,其他数字表示 wi,其中最后一个 \(0\) 是最后一个部件),那么六花打牌的过程可以为:

  • 取一张牌,手中的牌为 \(\{4\}\)
  • 打出 \(\{4\}\),再取四张牌,手中的牌为 \(\{0,0,2,0\}\)
  • 打出 \(\{2\}\),还需要再取两张,这时已摸到最后一个部件,六花胜利。
  • 而如果牌堆是 \(\{2,0,0,4,0,0,0\}\),不难发现是勇太的胜利。

现在,六花想要知道这 \(m!\) 种不同的牌堆中,有多少种能够让她胜利。

\(n\leq 40,w_i\leq 10^5\)

思路

不难发现一个排列时不合法的当且仅当存在一个位置 \(i\),前 \(i\) 个数的和小于 \(i\)。也就是减去 \(i\) 小于 \(0\)
减去 \(i\) 并不好统计,所以我们直接把所有数字减一,问题转化为有多少个排列满足任意前缀和非负。
不难发现,对于一个数字 \(k\),合法序列中它一定不排在最后的 \(k\) 位,不然显然前面至少有一个位置的前缀和小于 \(0\)
也就是说,每一个合法序列的最后一个位置一定是 \(-1\)。那么这个合法序列的所有循环同构中,有且仅有它自己是合法的。
所以我们只需要考虑有多少个本质不同的排列,其中两个排列本质不同定义为它们循环同构不同。
考虑 \(-1\) 的数量,显然答案就是 \(\frac{m!}{m-n+1}\)
只会看题解之后复述结论爬了 /kel

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MOD=998244353;
ll n,m,fac;

int main()
{
	scanf("%lld",&n);
	for (int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		m+=x;
	}
	fac=1;
	for (int i=1;i<=m;i++)
		if (i!=m-n+1) fac=fac*i%MOD;
	printf("%lld\n",fac);
	return 0;
}
posted @ 2021-01-27 23:24  stoorz  阅读(196)  评论(0编辑  收藏  举报