hdu6036 Division Game 容斥+组合数学+NTT

版权声明:转吧转吧这条东西只是来搞笑的。。 https://blog.csdn.net/jpwang8/article/details/89322174

Description


有0~k-1共k束花,每一束花中有m种颜色的花,第i种颜色有e[i]朵
第x次操作将会从第x%k束花中摘走至少一朵花,当一朵花被摘完游戏结束
对于i=0~k-1输出游戏在第i个位置恰好结束的方案数

Solution


每次至少摘一朵,那么游戏至多进行 n = i = 1 m e i n=\sum_{i=1}^{m}e_i
f ( x ) f(x) 表示在某个位置第x轮恰好结束的方案数,可以发现 f ( x ) f(x) 同时也对应恰好在第x轮结束的方案数,我们钦定最后一次全拿完就可以了
于是第i个位置的答案就是 j = 0 n f ( j + 1 ) i 1 f ( j ) k j + 1 \sum\limits_{j=0}^{n}{f\left(j+1\right)}^{i-1}{f\left(j\right)}^{k-j+1}
考虑怎么求 f ( x ) f(x) ,我们可以看成是把x个球染上m种颜色分到k个盒子里面,要求每个盒子里不同颜色的球数量之和非零,而某种颜色的球在某个盒子里可以不放。
我们设 g ( x ) = i = 1 m ( e i + x 1 x 1 ) g(x)=\prod\limits_{i=1}^{m}\binom{e_i+x-1}{x-1} ,那么 f ( x ) f(x) 就可以枚举空的位置容斥求了。于是有 f ( x ) = y = 0 x ( 1 ) x y ( x y ) g ( y ) f(x)=\sum\limits_{y=0}^{x}{{\left(-1\right)}^{x-y}\binom{x}{y}g(y)}

Code


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int MOD=985661441;
const int N=2000005;

int rv[N],p[N],e[N];
LL fac[N],inv[N],f[N],g[N];

void upd(LL &x,LL v) {
	x+=v,(x>=MOD)?(x-=MOD):0;
}

LL ksm(LL x,LL dep) {
	LL res=1;
	for (;dep;dep>>=1,x=x*x%MOD) {
		(dep&1)?(res=res*x%MOD):0;
	}
	return res;
}

void NTT(LL *a,int n,int f) {
	for (int i=0;i<n;++i) if (i<rv[i]) std:: swap(a[i],a[rv[i]]);
	for (int i=1;i<n;i<<=1) {
		LL wn=ksm(3,(f==1)?((MOD-1)/i/2):(MOD-1-(MOD-1)/i/2));
		for (int j=0;j<n;j+=(i<<1)) {
			LL w=1;
			for (int k=0;k<i;++k,w=w*wn%MOD) {
				LL u=a[j+k],v=a[j+k+i]*w%MOD;
				upd(a[j+k],v),a[j+k+i]=MOD-v;
				upd(a[j+k+i],u);
			}
		}
	}
	if (f==-1) {
		LL ny=ksm(n,MOD-2);
		for (int i=0;i<n;++i) a[i]=a[i]*ny%MOD;
	}
}

LL C(int n,int m) {
	if (m>n||m<0) return 0;
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}

int main(void) {
	freopen("data.in","r",stdin);
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	rep(i,2,N-1) {
		fac[i]=fac[i-1]*i%MOD;
		inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	}
	rep(i,2,N-1) inv[i]=inv[i-1]*inv[i]%MOD;
	int n,m,k,T=0;
	while (~scanf("%d%d",&m,&k)) { n=0;
		rep(i,1,m) {scanf("%d%d",&p[i],&e[i]),n+=e[i];}
		int len=1,lg=0;
		for (;len<=n*2;) len<<=1,++lg;
		for (int i=0;i<len;++i) rv[i]=(rv[i>>1]>>1)|((i&1)<<(lg-1));
		for (int i=0;i<len;++i) f[i]=g[i]=0;
		rep(i,0,n) g[i]=1;
		rep(i,0,m) rep(j,0,n) g[j]=g[j]*C(e[i]+j-1,j-1)%MOD;
		rep(i,0,n) {
			f[i]=inv[i]; (i&1)?(f[i]=MOD-f[i]):0;
			g[i]=g[i]*inv[i]%MOD;
		}
		NTT(f,len,1),NTT(g,len,1);
		for (int i=0;i<len;++i) f[i]=f[i]*g[i]%MOD;
		NTT(f,len,-1);
		rep(i,0,n) f[i]=f[i]*fac[i]%MOD; f[n+1]=0;
		printf("Case #%d: ", ++T);
		rep(i,1,k) {
			LL ans=0;
			rep(j,0,n) upd(ans,ksm(f[j+1],i-1)*ksm(f[j],k-i+1)%MOD);
			if (i==k) printf("%lld\n", ans);
			else printf("%lld ", ans);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/89322174
今日推荐