染色:多项式,分治,二项式反演,容斥原理,(生成函数?)

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
 2 #include
 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>1]>>1|(i&1?len>>1:0),swap(a[i],a[max(rev[i],i)]);
13     for(int mid=1;mid1)
14         for(int i=0,t=qp(3,opt*(mod-1)/mid/2+mod-1);i1)
15             for(int j=0,w=1,x,y;jmod)
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);imod;
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;i0,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;imod;
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
 2 #include
 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>1]>>1|(i&1?len>>1:0),swap(a[i],a[max(rev[i],i)]);
13     for(int mid=1;mid1)
14         for(int i=0,t=qp(3,opt*(mod-1)/mid/2+mod-1);i1)
15             for(int j=0,w=1,x,y;jmod)
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);imod;
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;i1);
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 }
updated

你可能感兴趣的:(染色:多项式,分治,二项式反演,容斥原理,(生成函数?))