码迷,mamicode.com
首页 > 其他好文 > 详细

「HAOI2018」染色

时间:2020-01-31 14:31:32      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:max   cli   names   void   print   splay   ace   sum   turn   

题目链接:Click here

Solution:

看到恰好,首先考虑容斥,设\(f[i]\)表示我们钦定\(i\)种颜色在序列中恰好出现了\(S\)次有多少种方案

那么现在就有\(i+1\)个部分,把他看作是可重集的全排列,方案数即 \({n! \over (S!)^i (n-Si)}\) ,后面每个都可以选 \((m-i)\) 种颜色

那么\(f[i]\)就是这样算的:
\[ f[i]={m\choose i}\times {n! \over (S!)^i (n-Si)!} \times (m-i)^{n-Si} \]
现在我们来考虑容斥,\(ans[i]\)表示恰好有\(i\)种颜色在序列中出现了恰好\(S\)次的方案,式子前乘的\({j\choose i}\)代表钦定的颜色选取方案
\[ ans[i]=\sum_{j=i}^{lim} (-1)^{j-i}{j\choose i} f[j]\ans[i]=\sum_{j=i}^{lim} (-1)^{j-i}{j! \over i! (j-i)!} f[j]\ans[i]\times i!=\sum_{j=i}^{lim} (-1)^{j-i}{j! \over (j-i)!} f[j]\\]
这里的\(lim=\min(\lfloor {n \over S}\rfloor , m)\),我们令\(A(x)=\sum_{i=0} ^{lim} (-1)^i i! x^i\),在令\(A=reverse(A)\),同时令\(B(x)=\sum_{i=0}^{lim} i!f[i] x^i\)

那么式子就转化成了这个样子:
\[ ans[i]\times i!=\sum_{j=i}^{lim} A_{lim+i-j} B_j \]
这显然是个卷积的形式,于是我们直接上\(NTT\)即可

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1004535809;
const int N=1e7+11;
const int M=4e5+11;
int n,m,s,ans,len=1,tim,lim,p[M];
int fac[N],ifac[N],W[M],g[M],f[M];
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int qpow(int x,int y){
    int re=1;
    while(y>0){
        if(y&1) re=re*x%mod;
        y>>=1;x=x*x%mod;
    }return re;
}
int C(int x,int y){
    if(x<y) return 0;
    return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
void NTT(int *a,int flag){
    for(int i=0;i<len;i++)
        if(i<p[i]) swap(a[i],a[p[i]]);
    for(int l=2;l<=len;l<<=1){
        int wn=qpow(3,(mod-1)/l);
        if(flag==-1) wn=qpow(wn,mod-2);
        for(int st=0;st<len;st+=l){
            int w=1;
            for(int u=st;u<st+(l>>1);u++,w=w*wn%mod){
                int x=a[u],y=w*a[u+(l>>1)]%mod;
                a[u]=(x+y)%mod;a[u+(l>>1)]=(x+mod-y)%mod;
            }
        }
    }
}
signed main(){
    n=read(),m=read(),s=read();
    for(int i=0;i<=m;i++) W[i]=read();
    lim=min(n/s,m);++lim;
    while(len<(lim<<1)) len<<=1,++tim;
    --lim;
    for(int i=0;i<len;i++)
        p[i]=(p[i>>1]>>1)|((i&1)<<(tim-1));
    fac[0]=1;ifac[0]=1;
    for(int i=1;i<=max(len,max(n,m));i++) fac[i]=fac[i-1]*i%mod;
    ifac[max(len,max(n,m))]=qpow(fac[max(len,max(n,m))],mod-2);
    for(int i=max(len,max(n,m))-1;i;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
    for(int i=0;i<=lim;i++){
        int inv=qpow(fac[s],i);
        inv=qpow(inv,mod-2);
        f[i]=C(m,i)*fac[n]%mod;
        f[i]=f[i]*inv%mod*ifac[n-s*i]%mod;
        f[i]=f[i]*qpow(m-i,n-s*i)%mod;
    }
    for(int i=0;i<=lim;i++) f[i]=f[i]*fac[i]%mod;
    for(int i=0;i<=lim;i++) g[lim-i]=(((i&1)?-1:1)*ifac[i]+mod)%mod;
    NTT(f,1);NTT(g,1);
    for(int i=0;i<=len;i++) f[i]=f[i]*g[i]%mod;
    NTT(f,-1);int invL=qpow(len,mod-2);
    for(int i=0;i<=len;i++) f[i]=f[i]*invL%mod;
    for(int i=0;i<=m;i++) ans=(ans+(f[lim+i]*ifac[i]%mod*W[i]%mod))%mod;
    printf("%lld\n",ans);
    return 0;
}

「HAOI2018」染色

标签:max   cli   names   void   print   splay   ace   sum   turn   

原文地址:https://www.cnblogs.com/NLDQY/p/12245139.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!