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);
}