题解 P5827【点双连通图计数】

Link

Update 2024.03.26:修改一处式子错误。

题意

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

1n105

思路

前置

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


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

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

对于一个有根无向连通图,每个点都会被包含在至少 1 个点双中,且每个点双的大小不少于 2。注意还需要特判 n=1 时输出 1

考虑断掉根连出的边,无向连通图被划分成若干连通块,根据 exp 的集合意义,求出单个连通块的 EGF 即可。

对于根所在的任意一个点双连通块,若在其任何除根以外的一点连上一个以该点为根的无向连通图不会影响根所在的任意一个点双的大小,故单个连通块的 EGF

n1gn+1Fn(x)n!

可以直接得到

F(x)=xexp(n1gn+1Fn(x)n!)=xexp(G(F(x)))lnF(x)x=G(F(x))

H(x)=lnF(x)x,有 G(F(x))=H(x),根据扩展拉格朗日反演有

G(x)=1n[xn1]H(x)xnFn(x)=1n[xn1]H(x)exp(ln((F(x)x)n))=1n[xn1]H(x)exp(nH(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],tmp3[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));
	m--;
	if(!m) return 1;
	for(int i=0;i<L;i++)
		g[i]=1ll*(mod-m)*tmp3[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]*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);
	Der(F,F,L);
	Log(L,F,tmp3);
	Der(tmp3,tmp2,L);
	NTT(tmp2,1);
	for(int i=0;i<5;i++)
		write(solve(ask[i])),putc('\n');
	flush();
	return 0;
}
posted @   ffffyc  阅读(21)  评论(2编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示