bzoj5306: [Haoi2018]染色
题目描述:
有一块长度为 $n$ 的画布,每个位置可以染成 $[1,m]$ 这些颜色中的一种。 如果画布上恰好有 $k$ 种颜色恰好出现了 $s$ 次,则会产生 $w_k$ 的愉悦度,求所有不同画布的愉悦度之和,对 $1004535809$ 取模。
思路:
记 $F_i$ 表示出现了 $s$ 次的颜色有 $i$ 种。于是有
$$
Ans=\sum_{i=1}^{n}w_i\times F_i
$$
考虑容斥计算表示 $F_i$ ,
$$
F_i=\sum_{j=i}^{n}(-1)^{j-i}C_{m}^{j}C_{n}^{j*s}\frac{(js)!}{(s!)^{j}}(m-j)^{n-js}C_{j}^{i}
$$
$C_{m}^{j}$ 表示从 $m$ 种颜色选中 $j$ 种颜色。
$C_{n}^{js}$ 表示从 $n$ 个位置中选出 $js$ 个。
$ \frac{(js)!}{(s!)^{j}}$ 表示 $js$ 个位置的排列方式。
$(m-j)^{n-j*s}$ 表示剩余的位置可以从 $(m-j)$ 个颜色中任选。
$C_{j}^{i}$ 是充斥系数
式子化简后
$$
F_{i}=\frac{m!n!}{i!}\sum_{j=1}^{n}\frac{(m-j)^{n-js}}{(j-i)!(m-j)!(n-js)!(s!)^{j}}
$$
所以
$$
Ans=\sum_{i=0}^{n}m!n!\frac{w_i}{i!}\sum_{j=i}^{n}(-1)^{j-i}\frac{1}{(j-i)!(m-j)!}
$$
提前第二个 $\sum$
$$
Ans=m!n!\sum_{j=0}^{n}\frac{(m-j)^{n-js}}{(m-j)!(n-js)!(s!)^{j}}\sum_{i=0}^{j}\frac{wi}{i!}\frac{(-1)^{j-i}}{(j-i)!}
$$
发现后半部分式子可以卷积
效率 $O(nlogn)$
以下代码:
#include<bits/stdc++.h> #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=4e5+5,M=1e7+2,p=1004535809; int n,m,s,jc[M],a[N],b[N],G[2],t,l,v[N],ny[M],w[N]; il int read(){ int x,f=1;char ch; _(!)ch=='-'?f=-1:f;x=ch^48; _()x=(x<<1)+(x<<3)+(ch^48); return f*x; } il int mu(int x,int y){ return x+y>=p?x+y-p:x+y; } il int ksm(LL a,int y){ LL b=1; while(y){ if(y&1)b=b*a%p; a=a*a%p;y>>=1; } return b; } il void ntt(int *x,int op){ for(int i=0;i<t;i++)if(i<v[i])swap(x[i],x[v[i]]); for(int wn,i=1;i<t;i<<=1){ wn=ksm(G[op],(p-1)/(i<<1)); for(int j=0;j<t;j+=(i<<1)){ for(int k=0,w=1;k<i;k++,w=1ll*wn*w%p){ int A=x[j+k],B=1ll*x[i+j+k]*w%p; x[j+k]=mu(A,B);x[i+j+k]=mu(A,p-B); } } } if(op){ int k=ksm(t,p-2); for(int i=0;i<t;i++)x[i]=1ll*x[i]*k%p; } } int main() { n=read();m=read();s=read();int R=min(m,n/s); G[0]=3;G[1]=ksm(G[0],p-2);int L=max(n,max(m,s)); for(int i=0;i<=m;i++)w[i]=read(); jc[0]=1;for(int i=1;i<=L;i++)jc[i]=1ll*i*jc[i-1]%p; ny[L]=ksm(jc[L],p-2);for(int i=L;i;i--)ny[i-1]=1ll*i*ny[i]%p; for(int i=0;i<=R;i++)a[i]=1ll*w[i]*ny[i]%p; for(int i=0;i<=R;i++)b[i]=(i&1)?p-ny[i]:ny[i]; t=1;while(t<=(R<<1))t<<=1,l++; for(int i=0;i<t;i++)v[i]=(v[i>>1]>>1)|((i&1)<<l-1); ntt(a,0);ntt(b,0); for(int i=0;i<t;i++)a[i]=1ll*a[i]*b[i]%p; ntt(a,1);int ans=0; for(int i=0;i<=R;i++) ans=mu(ans,1ll*ksm(m-i,n-i*s)*a[i]%p*ny[m-i]%p*ny[n-i*s]%p*ksm(ny[s],i)%p); ans=1ll*ans*jc[n]%p*jc[m]%p; printf("%d\n",ans); return 0; }