bzoj5306:[Haoi2018]染色

传送门

思路还是很巧妙的。

显然能对答案产生贡献的最多颜色为\(min\{m,n/S\}\)

\(f(i)\)为出现次数为\(S\)的颜色种类至少有\(i\)种的涂色方案

那么

\[f(i)=\binom{m}{i}\frac{n!}{(S!)^i(n-iS)!}(m-i)^{n-iS} \]

然后让我们分析一下这个式子,\(\binom{m}{i}\)很显然

重点是\(\frac{n!}{(S!)^i(n-iS)!}\)是怎么推出来的

我们发现对于那些恰好出现了\(S\)次的颜色

首先这\(iS\)个位置的选法是\(\binom{n}{iS}=\frac{n!}{(n-iS)!(iS)!}\)

然后对于这\(i\)种颜色的排布,我们可以列出它的方案数是等于

\[\frac{(iS)!}{(iS-S)!S!}\frac{(iS-S)!}{(iS-2S)!S!}\frac{(iS-2S)!}{(iS-3S)!S!}...\\ \frac{(iS)!}{S!}\frac{1}{S!}\frac{1}{S!}...\\ \frac{(iS)!}{(S!)^i} \]

然后就可以得出来一共的方案数为\(\frac{n!}{(S!)^i(n-iS)!}\)

然后对于剩下的\((n-iS)\)个位置,剩下的\((m-i)\)种颜色随便放,就是\((m-i)^{n-iS}\)

于是\(f(i)\)就推出来了


然后用一个很简单的容斥计算答案

\[ans(i)=\sum_{j=i}^{min(m,n/s)}(-1)^{j-i}\binom{j}{i}f(j) \]

看到组合数就要拆

\[ans(i)=\sum_{j=i}^{min(m,n/s)}(-1)^{j-i}\frac{j!}{i!(j-i)!}f(j)\\ i!*ans(i)=\sum_{j=i}^{min(m,n/s)}\frac{(-1)^{j-i}}{(j-i)!}f(j)j!\\ \]

\[A(x)=\frac{(-1)^x}{x!}\\ B(x)=f(x)x! \]

那么

\[i!*ans(i)=\sum_{j=i}^{min(m,n/s)}A(j-i)B(j)\\ i!*ans(i)=\sum_{j=0}^{min(m,n/s)-i}A(j)B(j+i)\\ \]

然后显然就是将\(B\)给reverse一下

\[i!*ans(i)=\sum_{j=0}^{min(m,n/s)-i}A(j)B(min(m,n/s)-j-i)\\ \]

显然就是一个卷积的形式了,NTT做完之后再reverse回来

\(D\)是做完之后的多项式

\[ans=\sum_{i=0}^{min(m,n/s)}\frac{w(i)D(i)}{i!} \]

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
	char ch; bool ok;
	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
const int maxn=5e5+10,maxm=1e7+10,mod=1004535809,g=3,gi=334845270;
#define rg register
int tot,r[maxm],len,n,m,ans,s,w[maxm],N,f[maxm],A[maxm],B[maxm],fac[maxm],inv[maxm];
int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int del(int x,int y){return x-y<0?x-y+mod:x-y;}
int mi(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=mul(ans,a);
		b>>=1,a=mul(a,a);
	}
	return ans;
}
void ntt(int *a,int f){
	for(rg int i=0;i<N;i++)if(r[i]>i)swap(a[i],a[r[i]]);
	for(rg int i=1;i<N;i<<=1){
		int wn=mi(f?g:gi,(mod-1)/(i<<1));
		for(rg int j=0;j<N;j+=(i<<1)){
			int w=1;
			for(rg int k=0;k<i;k++){
				int x=a[j+k],y=mul(w,a[j+k+i]);
				a[j+k]=add(x,y),a[j+k+i]=del(x,y),w=mul(w,wn);
			}
		}
	}
	if(f)return ;int inv=mi(N,mod-2);
	for(rg int i=0;i<N;i++)a[i]=mul(a[i],inv);
}
int C(int x,int y){return mul(fac[x],mul(inv[y],inv[x-y]));}
int main()
{
	read(n),read(m),read(s);N=min(n/s,m);
	fac[0]=inv[0]=1;int now=max(n,m);
	for(rg int i=1;i<=now;i++)fac[i]=mul(fac[i-1],i);
	inv[now]=mi(fac[now],mod-2);
	for(rg int i=now-1;i;i--)inv[i]=mul(inv[i+1],i+1);
	for(rg int i=0;i<=m;i++)read(w[i]);
	for(rg int i=0;i<=N;i++)
		f[i]=mul(C(m,i),mul(fac[n],mul(inv[n-i*s],mul(mi(mi(fac[s],i),mod-2),mi(m-i,n-i*s)))));
	for(rg int i=0;i<=N;i++)A[i]=mul((i&1?mod-1:1),inv[i]),B[i]=mul(f[i],fac[i]);
	reverse(B,B+N+1);tot=N*2;for(N=1;N<=tot;N<<=1)len++;
	for(rg int i=0;i<N;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
	ntt(A,1),ntt(B,1);
	for(rg int i=0;i<N;i++)A[i]=mul(A[i],B[i]);
	ntt(A,0);N=tot>>1;reverse(A,A+N+1);
	for(rg int i=0;i<=N;i++)ans=add(ans,mul(w[i],mul(A[i],inv[i])));
	printf("%d\n",ans);
}
posted @ 2019-05-08 21:48  蒟蒻--lichenxi  阅读(153)  评论(0编辑  收藏  举报