染色:多项式,分治,二项式反演,容斥原理,(生成函数?)
Description
为了报答小 C 的苹果, 小 G 打算送给热爱美术的小 C 一块画布, 这块画布可以抽象为一个长度为n的序列, 每个位置都可以被染成m种颜色中的某一种.
然而小 C 只关心序列的n个位置中出现次数恰好为s的颜色种数, 如果恰好出现了s次的颜色有k种, 则小C会产$W_k$的愉悦度.
小 C 希望知道对于所有可能的染色方案, 他能获得的愉悦度的和对1004535809取模的结果是多少.
$n \le 10^7$ $m \le 10^5$ $s \le 150$
总感觉这个s的范围可以搞一些事情,然而并不可以。
题目的含义中有两个「恰好」,想办法干掉。
其中「恰好s次」这个好解决,根据含义来就好了。
另一个「恰好k种」转化为「至少k种」然后容斥解决。
首先设$f_i$表示恰好出现s次的颜色至少有k种时的方案数,根据含义列式:
$f_i=C_{m}^{i} \times C_{n}^{i \times s} \times \frac{(i \times s)!}{(s!)^i} \times (m-i)^{n-i \times s} $
就是先选出$i \times s$个位置,再选出这$i$种颜色,然后把这些颜色排布在这些位置里,最后再在其它位置随意填充其他颜色。
这个肯定是有重复的,因为你在其他位置随意填充时可能出现了其它的恰好出现了s次的颜色。
具体重复了多少?
我们在考虑$f_i$时,对于任意的$j>i$,都可以从那j种颜色里选出i个,这些肯定是重复计算了。
所以$f_i$里实际上包含了多余的$\sum\limits_{j=i+1}^{m} C_{j}^{i} \times f_j $
(注意这里的f的容斥可以迭代下去,所以其实严谨意义上来说并不是$f_j$)
我们设$g_i$表示出现s次的颜色恰好有i个的方案数,那么就得到:
$g_i=f_i -\sum\limits_{j=i+1}^{m} g_j \times C_{j}^{i}$
这个转移包含了自我转移,不难想到分治FFT。(然后我就偏离了正轨)
可以发现式子的分母上有一些麻烦的$i!$,把它们弄出来。设$h_i=f_i \times i!$
转移式变成了$h_i = f_i \times i! - \sum\limits_{j=i+1}^{m} \frac{h_j}{(j-i)!}$
现在后面和式里面的东西就非常卷积了,然后就可以分治FFT了。
复杂度是$O(nlog^2n)$。卡卡常还6000ms+
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 #define int long long 5 #define mod 1004535809 6 int fac[10000005],inv[10000005],n,m,s,w[10000005],f[10000005],ans; 7 int rev[270005],len,a[270005],b[10000005],c[270005]; 8 int qp(int b,int t,int a=1){if(t<0)return 0;for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;} 9 int C(int b,int t){return t>b?0:fac[b]*inv[t]%mod*inv[b-t]%mod;} 10 int g(int i){return C(m,i)*C(n,i*s)%mod*fac[i*s]%mod*qp(inv[s],i)%mod*qp(m-i,n-i*s)%mod;} 11 void NTT(int *a,int opt){ 12 for(int i=1;i<len;++i)rev[i]=rev[i>>1]>>1|(i&1?len>>1:0),swap(a[i],a[max(rev[i],i)]); 13 for(int mid=1;mid<len;mid<<=1) 14 for(int i=0,t=qp(3,opt*(mod-1)/mid/2+mod-1);i<len;i+=mid<<1) 15 for(int j=0,w=1,x,y;j<mid;++j,w=w*t%mod) 16 x=a[i+j],y=a[i+j+mid]*w%mod,a[i+j]=(x+y)%mod,a[i+j+mid]=(x-y+mod)%mod; 17 if(opt==-1)for(int i=0,iv=qp(len,mod-2);i<len;++i)a[i]=a[i]*iv%mod; 18 } 19 void solve(int l,int r){ 20 if(l==r){f[l]=(g(l)*fac[l]-f[l]+mod)%mod;return;} 21 int md=l+r>>1;solve(md+1,r); 22 len=1;while(len<=r-l+1)len<<=1; 23 for(int i=0;i<len;++i)a[i]=0,b[i]=inv[i]; 24 for(int i=r;i>md;--i)a[r-i]=f[i]; 25 NTT(a,1);NTT(b,1); 26 for(int i=0;i<len;++i)c[i]=a[i]*b[i]%mod; 27 NTT(c,-1); 28 for(int i=r-md;i<=r-l;++i)f[r-i]=(f[r-i]+c[i])%mod; 29 solve(l,md); 30 } 31 main(){ 32 fac[0]=1; 33 scanf("%lld%lld%lld",&n,&m,&s);int U=max(n,m); 34 for(int i=0;i<=m;++i)scanf("%lld",&w[i]); 35 for(int i=1;i<=U;++i)fac[i]=fac[i-1]*i%mod; 36 inv[U]=qp(fac[U],mod-2); 37 for(int i=U-1;~i;--i)inv[i]=inv[i+1]*(i+1)%mod; 38 solve(0,m); 39 for(int i=0;i<=m;++i)ans=(ans+f[i]*inv[i]%mod*w[i])%mod; 40 printf("%lld\n",ans); 41 }
首先听NC大神说可以用生成函数解决大部分分治FFT问题,以大常数为代价换取$O(nlogn)$的优秀复杂度。
但是生成函数我还不是很会,或者说对于这道题来说我还没有构造出来,所以先鸽掉。
正解:
二项式反演的形式之一:
$f(n)=\sum\limits_{i=n}^{m} C_i^n g(i)$等价于$g(n)=\sum\limits_{i=n}^m (-1)^{i-n} C_n^i f(i)$
可以发现这个和我上面列出的$g_i$与$f_i$的式子有一些相像。
再粘一遍:$g_i=f_i -\sum\limits_{j=i+1}^{m} g_j \times C_{j}^{i}$
把f和g分别放到两侧:$f_i= \sum\limits_{j=i}^m C_j^i g_i$
然后套用二项式反演的式子,得到$g_i=\sum\limits_{j=i}^m (-1)^{j-i} \times C_j^i \times f_j$
然后把式子完全展开,就可以FFT了。
复杂度$O(nlogn)$。代码待补。搞定。2500ms。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 #define int long long 5 #define mod 1004535809 6 int fac[10000005],inv[10000005],n,m,s,w[10000005],f[270005],ans; 7 int rev[270005],len,a[270005]; 8 int qp(int b,int t,int a=1){if(t<0)return 0;for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;} 9 int C(int b,int t){return t>b?0:fac[b]*inv[t]%mod*inv[b-t]%mod;} 10 int g(int i){return C(m,i)*C(n,i*s)%mod*fac[i*s]%mod*qp(inv[s],i)%mod*qp(m-i,n-i*s)%mod;} 11 void NTT(int *a,int opt){ 12 for(int i=1;i<len;++i)rev[i]=rev[i>>1]>>1|(i&1?len>>1:0),swap(a[i],a[max(rev[i],i)]); 13 for(int mid=1;mid<len;mid<<=1) 14 for(int i=0,t=qp(3,opt*(mod-1)/mid/2+mod-1);i<len;i+=mid<<1) 15 for(int j=0,w=1,x,y;j<mid;++j,w=w*t%mod) 16 x=a[i+j],y=a[i+j+mid]*w%mod,a[i+j]=(x+y)%mod,a[i+j+mid]=(x-y+mod)%mod; 17 if(opt==-1)for(int i=0,iv=qp(len,mod-2);i<len;++i)a[i]=a[i]*iv%mod; 18 } 19 main(){ 20 fac[0]=len=1; 21 scanf("%lld%lld%lld",&n,&m,&s);int U=max(n,m);while(len<=m<<1)len<<=1; 22 for(int i=1;i<=U;++i)fac[i]=fac[i-1]*i%mod; 23 inv[U]=qp(fac[U],mod-2); 24 for(int i=U-1;~i;--i)inv[i]=inv[i+1]*(i+1)%mod; 25 for(int i=0;i<=m;++i)a[i]=inv[i]*(i&1?mod-1:1)%mod,f[i]=g(m-i)*fac[m-i]%mod; 26 NTT(a,1);NTT(f,1);for(int i=0;i<len;++i)f[i]=a[i]*f[i]%mod;NTT(f,-1); 27 for(int i=0,w;i<=m;++i)scanf("%lld",&w),ans=(ans+f[m-i]*inv[i]%mod*w)%mod; 28 printf("%lld\n",ans); 29 }