题解 P5828【边双连通图计数】

Link

题意

n 个点的有标号边双连通图数量,对 998244353 取模。

1n105

思路

前置

有标号无向连通图计数扩展拉格朗日反演


设有根无向连通图的 EGFF(x),有根边双连通图的 EGFG(x)

我们显然可以使用有标号无向连通图EGF 乘上 n 以得到 F(x),于是考虑用 F,G 互相表示。

对于一个有根无向连通图,我们可以枚举其根所在的边双大小,再算出所有连向其它部分的边的 EGF

对于一条边,我们可以有:其挂着一个无向连通图;可以连在边双中任意一个点上。所以一条边的 EGFnF(x)。(其中 n 为根节点所在的边双的大小)

由于连的多条边与一条边也可看为集合与元素的关系,可以直接得到F(x)=n1gnexp(nF(x))xnn!=n1gnn!xnenF(x)=n1gnn!xneF(x)n=n1gnn!(xexp(F(x)))n=G(xexp(F(x)))H(x)=xexp(F(x)),有 F(x)=G(H(x)),根据扩展拉格朗日反演有G(x)=1n[xn1]F(x)xnHn(x)=1n[xn1]F(x)exp(nF(x)) 至此直接求解即可。

时间复杂度 O(nlogn)

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int mod=998244353,N=524288+10;
namespace Poly{
    int n,m,a[N],b[N],c[N],s[N],ss[N],d[N],e[N];
    int f[N],g[N],h[N],F[N],tmp1[N],tmp2[N];
    int rev[N],inv[N],fac[N],ifac[N],G[19][N],lim;
    inline void init(int n,int mode=1){
        if(mode){
            int l=0;
            for(lim=1;lim<n;lim<<=1)l++;
            for(int i=1;i<lim;i++)
                rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
        }else{
            for(lim=1;lim<n;lim<<=1);
        }
    }
    inline int qpow(int x,int y){
        int res=1;
        while(y){
            if(y&1) res=1ll*res*x%mod;
            x=1ll*x*x%mod;
            y>>=1;
        }
        return res;
    }
    inline void Prefix(int n){
        inv[1]=1;
        for(int i=2;i<=n;i++)
            inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
        fac[0]=1;
        for(int i=1;i<=n;i++)
            fac[i]=1ll*fac[i-1]*i%mod;
        ifac[n]=qpow(fac[n],mod-2);
        for(int i=n;i>=1;i--)
            ifac[i-1]=1ll*ifac[i]*i%mod;
        for(int i=1,p=1;i<=18;i++,p<<=1){
            G[i][0]=1;
            G[i][1]=qpow(3,mod-1>>i);
            for(int j=2;j<p;j++)
                G[i][j]=1ll*G[i][j-1]*G[i][1]%mod;
        }
    }
    inline void NTT(int *a,int t){
        for(int i=0;i<lim;i++)
            if(i<rev[i])
                swap(a[i],a[rev[i]]);
        for(int i=1,t=1;i<lim;i<<=1,t++){
            for(int j=0;j<lim;j+=i<<1){
                int t1,t2;
                for(int k=0;k<i;k++){
                    t1=a[j+k];
                    t2=1ll*G[t][k]*a[i+j+k]%mod;
                    a[j+k]=(t1+t2)%mod;
                    a[i+j+k]=(t1-t2+mod)%mod;
                }
            }
        }
        if(t==1) return ;
        int Inv=qpow(lim,mod-2);
        reverse(a+1,a+lim);
        for(int i=0;i<lim;i++)
            a[i]=1ll*a[i]*Inv%mod;
    }
    inline void Inv(int *a,int *b,int n){
        if(n==1){
            b[0]=qpow(a[0],mod-2);
            return ;
        }
        Inv(a,b,n+1>>1);
        init(n<<1);
        for(int i=0;i<n;i++)
            c[i]=a[i];
        for(int i=n;i<lim;i++)
            c[i]=0;
        NTT(c,1),NTT(b,1);
        for(int i=0;i<lim;i++)
            b[i]=1ll*(2-1ll*c[i]*b[i]%mod+mod)%mod*b[i]%mod;
        NTT(b,-1);
        for(int i=n;i<lim;i++)
            b[i]=0;
    }
    inline void Mul(int *a,int *b,int n){
        init(n<<1);
        memset(c,0,lim<<2);
        memcpy(c,b,n<<2);
        NTT(a,1),NTT(c,1);
        for(int i=0;i<lim;i++)
            a[i]=1ll*a[i]*c[i]%mod;
        NTT(a,-1);
    }
    inline void Der(int *a,int *b,int n){
        for(int i=1;i<n;i++)
            b[i-1]=1ll*i*a[i]%mod;
        b[n-1]=0;
    }
    inline void Int(int *a,int *b,int n){
        for(int i=1;i<n;i++)
            b[i]=1ll*a[i-1]*inv[i]%mod;
        b[0]=0;
    }
    inline void polyln(int *a,int *b,int n){
        static int p[N];
        memset(p,0,n<<3);
        memset(d,0,n<<3);
        Der(a,p,n);
        Inv(a,d,n);
        Mul(p,d,n);
        Int(p,b,n);
    }
    inline void Log(int n,int *f,int *g){
        init(n);
        polyln(f,g,lim);
        for(int i=n;i<lim;i++)
            g[i]=0;
    }
    inline void polyexp(int n,int *a,int *b){
        if(n==1){
            b[0]=1;
            return ;
        }
        polyexp(n+1>>1,a,b);
        Log(n,b,s);
        for(int i=0;i<n;i++)
            s[i]=a[i]>=s[i]?a[i]-s[i]:a[i]+mod-s[i];
        for(int i=n;i<lim;i++)
            b[i]=s[i]=0;
        s[0]++;
        NTT(s,1),NTT(b,1);
        for(int i=0;i<lim;i++)
            b[i]=1ll*b[i]*s[i]%mod;
        NTT(b,-1);
        for(int i=n;i<lim;i++)
            b[i]=0;
    }
    inline void Exp(int n,int *a,int *b){
        polyexp(n,a,b);
    }
}
using namespace Poly;
int ask[5],L;
inline int solve(int m){
    memset(g,0,sizeof(g));
    memset(tmp1,0,sizeof(tmp1));
    for(int i=0;i<L;i++)
        g[i]=1ll*(mod-m)*F[i]%mod;
    Exp(L,g,tmp1);
    init(L<<1);
    NTT(tmp1,1);
    for(int i=0;i<lim;i++)
        tmp1[i]=1ll*tmp1[i]*tmp2[i]%mod;
    NTT(tmp1,-1);
    return 1ll*tmp1[m-1]*inv[m]%mod*fac[m-1]%mod;
}
int main(){
    for(int i=0;i<5;i++)
        ask[i]=read(),n=max(n,ask[i]+1);
    init(n,0);
    L=lim;
    Prefix(L);
    for(int i=0;i<L;i++)
        f[i]=1ll*qpow(2,1ll*i*(i-1)/2%(mod-1))*ifac[i]%mod;
    Log(L,f,F);
    for(int i=0;i<L;i++)
        F[i]=1ll*F[i]*i%mod;
    Der(F,tmp2,L);
    NTT(tmp2,1);
    for(int i=0;i<5;i++)
        write(solve(ask[i])),putc('\n');
    flush();
    return 0;
}
posted @   ffffyc  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示