LGP4841题解

无向联通图计数板子

首先,这个太难了,先让我们来求一个简单的:

无向图计数。

一共 \(\frac {n \times (n+1)} 2\) 条可能存在的边,枚举一条边是否存在,就有 \(2^{\frac {n \times (n+1)} 2}\) 个无向图。

回到本题,我们先设 \(f_n\) 是我们要求的 \(n\) 个无向联通图的个数, \(g_n\)\(2^{\frac {n \times (n+1)} 2}\),很明显有:

\[g_n=\sum_{i=1}^n\binom{n-1}{i-1}f_ig_{n-i} \]

组合数后面的是一个卷积的形式,而组合数本身就是一堆阶乘,这在提示我们使用 EGF,并且本题的模数也是一个 NTT 模数。

\[\frac {g_n} {(n-1)!}=\sum_{i=1}^n \frac {f_i} {(i-1)!} \times \frac {g_{n-i}} {(n-i)!} \]

然后我们设 \(F(x)\) 是 { \(f_n\) } 的 EGF,\(G(x)\) 是 { \(g_n\) } 的 EGF。

很容易发现:\(g_n=[x^n]G'(x)\),然后就有:

\[G'(x)=F'(x)G(x) \]

\[\frac {G'(x)} {G(x)}=F'(x) \]

\[F(x)=\ln G(x) \]

然后我们就做完了。

贴代码:

#include<cstring>
#include<cstdio>
#define clr(f,len) memset(f,0,(len)<<2)
#define cpy(f,g,len) memcpy(f,g,(len)<<2)
const int G=3,invG=334845270,mod=1004535809,M=7e5+5;
int n,t[M],f[M],inv[M],tor[M];
inline void swap(int&a,int&b){
    a^=b^=a^=b;
}
inline void GetRev(int len){
    for(int i=0;i<len;++i)t[i]=t[i>>1]>>1|(i&1?len>>1:0);
}
inline void px(int*f,int*g,int len){
    for(int i=0;i<len;++i)f[i]=1ll*f[i]*g[i]%mod;
}
inline void der(int*f,int n){
    for(int i=1;i<n;++i)f[i-1]=1ll*f[i]*i%mod;
    f[n-1]=0;
}
inline void itg(int*f,int n){
    for(int i=n;i;--i)f[i]=1ll*f[i-1]*inv[i]%mod;
    f[0]=0;
}
inline int pow(int a,int b=mod-2){
    int ans=1;
    for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;
    return ans;
}
void NTT(int*f,bool flag,int n){
    int i,k,p,w,w1,len;
    for(i=0;i<n;++i)if(i<t[i])swap(f[i],f[t[i]]);
    for(p=2;p<=n;p<<=1){
        len=p>>1;w1=pow(flag?G:invG,(mod-1)/p);
        for(k=0;k<n;k+=p){
            w=1;
            for(i=k;i<k+len;++i){
                int t=1ll*f[i+len]*w%mod;
                if((f[i+len]=f[i]-t)<0)f[i+len]+=mod;
                if((f[i]=f[i]+t)>mod)f[i]-=mod;
                w=1ll*w*w1%mod;
            }
        }
    }
    if(flag)return;
    int invN=pow(n);
    for(i=0;i<n;++i)f[i]=1ll*f[i]*invN%mod;
}
void invp(int*f,int m){
    static int b1[M],b2[M],b3[M];
    int i,n=1,len;b1[0]=pow(f[0]);
    while(n<m)n<<=1;
    for(len=2;len<=n;len<<=1){
        for(i=0;i<(len>>1);++i)b3[i]=(b1[i]<<1)%mod;
        cpy(b2,f,len);
        GetRev(len<<1);
        NTT(b1,1,len<<1);px(b1,b1,len<<1);
        NTT(b2,1,len<<1);px(b1,b2,len<<1);
        NTT(b1,0,len<<1);clr(b1+len,len);
        for(i=0;i<len;++i)b1[i]=(b3[i]-b1[i]+mod)%mod;
    }
    cpy(f,b1,m);clr(b1,n<<1);clr(b2,n<<1);clr(b3,n<<1);
}
void lnp(int*f,int n){
    static int sav[M];
    cpy(sav,f,n);
    der(f,n);invp(sav,n);
    GetRev(n<<1);
    NTT(f,1,n<<1);NTT(sav,1,n<<1);
    px(f,sav,n<<1);NTT(f,0,n<<1);
    itg(f,n-1);
    clr(f+n,n);clr(sav,n);
}
signed main(){
    int i,m=1,tmp1,tmp2;
    inv[1]=tor[0]=tor[1]=1;
    scanf("%d",&n);++n;
    while(m<n)m<<=1;
    GetRev(m);
    for(i=2;i<=m;++i){
        inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
        tor[i]=1ll*tor[i-1]*i%mod;
    }
    for(i=0;i<m;++i)f[i]=1ll*pow(2,(1ll*i*(i-1)>>1)%(mod-1))*pow(tor[i])%mod;
    lnp(f,m);
    printf("%d",1ll*f[n-1]*tor[n-1]%mod);
}
posted @ 2022-01-10 15:41  Prean  阅读(26)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};