zoukankan      html  css  js  c++  java
  • HAOI2018 染色

    一道推柿子题目
    非常锻炼思维能力.
    题目链接


    首先,颜色次数显然不能多于(lim=min(frac{n}{s},m))个.
    由于问的是恰好(k)个,我们定义(f_i)表示出现次数为(s)的颜色恰好为(k)个的方案数.
    那么,(ans=sum_{i=0}^{lim}w_if_i)
    我们用二项式反演的思想,令(g_i)表示至少有(i)个颜色出现次数为(s)次.
    (g_i)如何求呢?
    显然,我们首先选出(i*s)个格子,再选出(i)种颜色,然后对其进行排列,剩下的格子用剩下的颜色乱填即可.
    (g_i=C_n^{i*s}*C_m^i*frac{(s*i)!}{(s!)^i}*(m-i)^{n-i*s})
    这里四个乘数对应上文所述,应该很好理解.
    显然,每个(f_i)会在(g_{j,jleq i})中计算(C_j^i)次.
    那么,(g_i=sum_{j=i}^{lim}C_j^if_i)
    接下来就是二项式反演环节.
    (f_i=sum_{j=i}^{lim}(-1)^{j-i}C_j^ig_j)

    我们回忆一下(ans)的计算.
    (ans=sum_{i=0}^{lim}w_if_i)
    (=sum_{i=0}^{lim}w_isum_{j=i}^{lim}(-1)^{j-i}C_j^ig_j)
    我们把(C_j^i)拆掉得
    (ans=sum_{i=0}^{lim}w_isum_{j=i}^{lim}(-1)^{j-i}frac{j!}{i!(j-i)!}g_j)
    (i!)提出来得(ans=sum_{i=0}^{lim}frac{w_i}{i!}sum_{j=i}^{lim}frac{(-1)^{j-i}}{(j-i)!}j!g_j)
    我们令多项式(A=sum_{i=0}^{lim}frac{(-1)^i}{i!}x^i,B=sum_{i=0}^{lim}i!g_i)
    (A)反转,然后(A*B)就是答案.
    这个用(NTT)优化即可.
    时间复杂度(O(n+m*log m))

    代码如下

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #define N (600010)
    #define M (10000010)
    #define P (1004535809)
    #define rg register int
    typedef long double ld;
    typedef long long LL;
    typedef unsigned long long ull;
    using namespace std;
    inline char read(){
        static const int IN_LEN=1000000;
        static char buf[IN_LEN],*s,*t;
        return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
    }
    template<class T>
    inline void read(T &x){
        static bool iosig;
        static char c;
        for(iosig=false,c=read();!isdigit(c);c=read()){
            if(c=='-')iosig=true;
            if(c==-1)return;
        }
        for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
        if(iosig)x=-x;
    }
    inline char readchar(){
        static char c;
        for(c=read();!isalpha(c);c=read())
        if(c==-1)return 0;
        return c;
    }
    const int OUT_LEN = 10000000;
    char obuf[OUT_LEN],*ooh=obuf;
    inline void print(char c) {
        if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
        *ooh++=c;
    }
    template<class T>
    inline void print(T x){
        static int buf[30],cnt;
        if(x==0)print('0');
        else{
            if(x<0)print('-'),x=-x;
            for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
            while(cnt)print((char)buf[cnt--]);
        }
    }
    inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
    int n,m,S,w[N],t,Lim,len,rev[N];
    LL mi[40],iv[40],ans,f[N],g[N],jc[M],inv[M];
    LL ksm(LL a,int p){
        LL res=1;
        while(p){
            if(p&1)res=(res*a)%P;
            a=(a*a)%P,p>>=1;
        }
        return res;
    }
    void NTT(LL *a,int tp){
        for(int i=0;i<Lim;i++)
        if(i<rev[i])swap(a[i],a[rev[i]]);
        for(int i=1,s=1;i<Lim;i<<=1,s++){
            LL w=(tp>0)?mi[s]:iv[s];
            for(int R=i<<1,j=0;j<Lim;j+=R){
                LL p=1;
                for(int k=j;k<j+i;k++,p=p*w%P){
                    LL x=a[k],y=p*a[k+i]%P;
                    a[k]=(x+y)%P,a[k+i]=(x-y+P)%P;
                }
            }
        }
        if(tp==-1){
            LL dv=ksm(Lim,P-2)%P;
            for(int i=0;i<Lim;i++)a[i]=a[i]*dv%P;
        }
    }
    LL C(int n,int m){return jc[n]*inv[n-m]%P*inv[m]%P;}
    int main(){
        read(n),read(m),read(S),jc[0]=inv[0]=inv[1]=1,t=min(n/S,m);
        for(int i=0;i<=m;i++)read(w[i]);
        for(int i=1;(1ll<<i)<=P;i++)mi[i]=ksm(3,(P-1)/(1<<i)),iv[i]=ksm(mi[i],P-2)%P;
        for(int i=1;i<=max(n,m);i++)jc[i]=jc[i-1]*i%P;
    	for(int i=2;i<=max(n,m);i++)inv[i]=inv[P%i]*(P-P/i)%P;
    	for(int i=1;i<=max(n,m);i++)inv[i]=inv[i-1]*inv[i]%P;
        for(int i=0;i<=t;i++)g[i]=jc[i]*C(m,i)%P*C(n,i*S)%P*jc[S*i]%P*ksm(ksm(jc[S],i),P-2)%P*ksm(m-i,n-i*S)%P;
        for(int i=0;i<=t;i++)f[t-i]=(i&1)?P-inv[i]:inv[i];
        for(Lim=1;Lim<=t+t;Lim<<=1)len++;
        for(int i=0;i<Lim;i++)rev[i]=((rev[i>>1])>>1)|((i&1)<<(len-1)); 
        NTT(f,1),NTT(g,1);
        for(int i=0;i<Lim;i++)f[i]=f[i]*g[i]%P;
        NTT(f,-1);
        for(int i=0;i<=t;i++)ans=(ans+1ll*w[i]*f[i+t]%P*inv[i]%P)%P;
        printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    LeetCode Best Time to Buy and Sell Stock
    LeetCode Scramble String
    LeetCode Search in Rotated Sorted Array II
    LeetCode Gas Station
    LeetCode Insertion Sort List
    LeetCode Maximal Rectangle
    Oracle procedure
    浏览器下载代码
    Shell check IP
    KVM- 存储池配置
  • 原文地址:https://www.cnblogs.com/Romeolong/p/10142454.html
Copyright © 2011-2022 走看看