zoukankan      html  css  js  c++  java
  • 染色:多项式,分治,二项式反演,容斥原理,(生成函数?)

    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} imes C_{n}^{i imes s} imes frac{(i imes s)!}{(s!)^i} imes (m-i)^{n-i imes s} $

    就是先选出$i imes s$个位置,再选出这$i$种颜色,然后把这些颜色排布在这些位置里,最后再在其它位置随意填充其他颜色。

    这个肯定是有重复的,因为你在其他位置随意填充时可能出现了其它的恰好出现了s次的颜色。

    具体重复了多少?

    我们在考虑$f_i$时,对于任意的$j>i$,都可以从那j种颜色里选出i个,这些肯定是重复计算了。

    所以$f_i$里实际上包含了多余的$sumlimits_{j=i+1}^{m} C_{j}^{i} imes f_j $

    (注意这里的f的容斥可以迭代下去,所以其实严谨意义上来说并不是$f_j$)

    我们设$g_i$表示出现s次的颜色恰好有i个的方案数,那么就得到:

    $g_i=f_i -sumlimits_{j=i+1}^{m} g_j imes C_{j}^{i}$

    这个转移包含了自我转移,不难想到分治FFT。(然后我就偏离了正轨)

    可以发现式子的分母上有一些麻烦的$i!$,把它们弄出来。设$h_i=f_i imes i!$

    转移式变成了$h_i = f_i imes i! - sumlimits_{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
    ",ans);
    41 }
    然而这个复杂度是不对的

    首先听NC大神说可以用生成函数解决大部分分治FFT问题,以大常数为代价换取$O(nlogn)$的优秀复杂度。

    但是生成函数我还不是很会,或者说对于这道题来说我还没有构造出来,所以先鸽掉。

    正解:

    二项式反演的形式之一:

    $f(n)=sumlimits_{i=n}^{m} C_i^n g(i)$等价于$g(n)=sumlimits_{i=n}^m (-1)^{i-n} C_n^i f(i)$

    可以发现这个和我上面列出的$g_i$与$f_i$的式子有一些相像。

    再粘一遍:$g_i=f_i -sumlimits_{j=i+1}^{m} g_j imes C_{j}^{i}$

    把f和g分别放到两侧:$f_i= sumlimits_{j=i}^m C_j^i g_i$

    然后套用二项式反演的式子,得到$g_i=sumlimits_{j=i}^m (-1)^{j-i}  imes C_j^i imes 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
    ",ans);
    29 }
    updated
  • 相关阅读:
    [转载]很完整的2、8、10、16进制转换方法
    [转载]C++星号的含义
    [转载]C++星号的含义
    [转载]麦凯66表格
    [转载]js入门·对象属性方法大总结
    [转载]图解《越狱》中纸玫瑰的折法
    [转载]比较开始日期与结束日期的js方法
    Cannot set device tcp segmentation offload settings: Invalid argument
    记一次薪酬谈判的教训
    TCP拥塞状态机的实现(中)
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12008901.html
Copyright © 2011-2022 走看看