[bzoj4735][数论]你的生命已如风中残烛

63 篇文章 0 订阅

Description

众所周知,萌萌哒六花不擅长数学,所以勇太给了她一些数学问题做练习。但是今天六花酱不想做数学题,于是他
们开始打牌。现在他们手上有m张不同的牌,牌有两种:普通牌和功能牌。功能牌一共有n张,每张功能牌都有一个
属性值wi,保证Sigma(wi)=m,1<=i<=N现在勇太将这m张牌随机打乱(一共有m!种不同的顺序)。一开始,六花先从
牌堆顶端取一张牌。接着每回合六花可以选择手中的一张牌打出,如果这张牌是普通牌,那么什么都不会发生;如
果这种牌是功能牌,那么六花需要从牌堆顶端再取wi张牌。重复这个过程直到六花手中没有手牌或六花要摸牌的时
候牌堆已经空了,如果是前者,则勇太胜利,否则六花胜利。举例来说,如果牌堆是{3,0,2,0,0)(用0表示
普通牌,其他数字表示wi),那么六花打牌的过程可以为: 1)取一张牌,手中的牌为{3}。
2)打出{3},再取三张牌,手中的牌为{0,2,0}。 3)打出这三张牌,还需要再取两张,取到第二张的时候牌堆中已没有牌,六花胜利。
而如果牌堆是{2,0,0,3,0},不难发现是勇太大胜利。现在,六花想要知道,这M!种顺序中,有多少种是能让自
己取得胜利的呢。当然这个问题对萌萌哒六花来说实在是太雉了,所以她来向你寻求帮助,你能帮帮她吗。

Input

第一行一个整数n。 第二行他个空格隔开的正整数wi。 通过输入你可以自己算出来m=Sigma(Wi),1<=i<=N
n≤40,1<wi≤10^5

Output

输出一个整数表示答案,答案可能很大,你只需要输出对998244353取模后的结果。

Sample Input

1

3

Sample Output

2

HINT

样例解释一

m!种牌堆中,{3,0,0),{0,3,0){0,0,3)各有两个,其中只有第一种满足条件。

题解

貌似网上没找到什么证明??我来瞎写一个qwq
首先可以把所有数都 − 1 -1 1,那么得到一个和为 0 0 0的数列,要求任意前缀和 &gt; = 0 &gt;=0 >=0
然后开始神奇操作…
在最后加一个数 − 1 -1 1,那么得到一个和为 − 1 -1 1的数列,要求在 [ 1 , m ] [1,m] [1,m]的任意前缀和 &gt; = 0 &gt;=0 >=0
把这 m + 1 m+1 m+1个数看成圆排列,那么共有 m ! m! m!种方案
考虑证明每个圆排列唯一对应了一种方案
显然对于一个圆排列,我们能找到一个连续的一段满足其和最小,不妨设开头结尾为 u , v u,v u,v,并且满足 u &lt; v u&lt;v u<v,其他情况类推
那么以 u − 1 u-1 u1为结尾的任意不超过 v v v后缀都会满足和 &gt; = 0 &gt;=0 >=0,以 v + 1 v+1 v+1为开头的任意不超过 u u u前缀都会满足和 &gt; = 0 &gt;=0 >=0
如果一刀切在了非 v v v v + 1 v+1 v+1之间,假设在 [ v + 1 , u − 1 ] [v+1,u-1] [v+1,u1]中,某个数 x x x的前面,显然 [ v + 1 , x ] [v+1,x] [v+1,x]这一段前缀不小于 0 0 0,又因为和为 − 1 -1 1,那么一定有一个不合法的位置在 v v v
[ u , v ] [u,v] [u,v]中切是同理的
故一个圆排列唯一对应一种方案
我们考虑怎么去除最后一个是我们加入的 − 1 -1 1的方案
加入这个 − 1 -1 1后共有 m − n + 1 m-n+1 mn+1 − 1 -1 1,显然我们所有合法方案结尾均为 − 1 -1 1,那么真正结尾为加入的 − 1 -1 1的方案被计数了 m − n + 1 m-n+1 mn+1
所以答案即为
m ! m − n + 1 \frac{m!}{m-n+1} mn+1m!

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int mod=998244353;
int pow_mod(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=1LL*ret*a%mod;
		a=1LL*a*a%mod;b>>=1;
	}
	return ret;
}
int getans(int n,int m)
{
	int ret=1;
	for(int i=1;i<=m;i++)ret=1LL*ret*i%mod;
	return 1LL*ret*pow_mod(m-n+1,mod-2)%mod;
}
int main()
{
	int n=read(),m=0;
	for(int i=1;i<=n;i++)m+=read();
	pr2(getans(n,m));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值