luogu P4233

题目链接

题意

从有哈密尔顿回路的竞赛图中随机选取一张,图上哈密尔顿回路数的期望是多少,如果没有这样的竞赛图,输出-1,mod 998244353

数据

给定n,表示竞赛图的点数,需要对从1到n的每个值都计算一次
n ≤ 1 e 5 n\le 1e5 n1e5

解法

考虑知道n个点的竞赛图中总共有多少条哈密尔顿回路,首先需要一个n个点的环排列,方案数是 ( n − 1 ) ! (n-1)! (n1)!,然后剩下来的边随便连,这是 2 n ∗ ( n − 1 ) / 2 − n 2^{n*(n-1)/2-n} 2n(n1)/2n,这个是每条边有两个方向,然后需要知道n个点的竞赛图中有多少个图存在哈密尔顿回路,设为f[n]
那么答案就是 ( n − 1 ) ! ∗ 2 n ∗ ( n − 1 ) / 2 − n f [ n ] \frac{(n-1)!*2^{n*(n-1)/2-n}}{f[n]} f[n](n1)!2n(n1)/2n

现在的问题是求f[n]的值,考虑用总数减去不合法的方案数:
f [ n ] = 2 C ( n , 2 ) − ∑ i = 1 n − 1 f [ i ] ∗ C ( n , i ) ∗ 2 C ( n − i , 2 ) f[n]=2^{C(n,2)}-\sum_{i=1}^{n-1}f[i]*C(n,i)*2^{C(n-i,2)} f[n]=2C(n,2)i=1n1f[i]C(n,i)2C(ni,2)
这个式子的前半部分是n个点的竞赛图总数,后面计算的方式是枚举一号点所在的合法联通块的大小,此时为了保证这个方案不合法,其他点向这个联通块连边的方式是固定的,其它的点之间可以随便连。

观察f[n]的式子,由于里面出现了组合数,考虑EGF,把组合数拆开:
f [ n ] = 2 C ( n , 2 ) − ∑ i = 1 n − 1 f [ i ] ∗ n ! i ! ∗ ( n − i ) ! ∗ 2 C ( n − i , 2 ) f[n]=2^{C(n,2)}-\sum_{i=1}^{n-1}f[i]*\frac{n!}{i!*(n-i)!}*2^{C(n-i,2)} f[n]=2C(n,2)i=1n1f[i]i!(ni)!n!2C(ni,2)
我们把 1 i ! \frac{1}{i!} i!1 f [ i ] f[i] f[i]放到一起, 1 ( n − i ! ) \frac{1}{(n-i!)} (ni!)1 2 C ( n − i , 2 ) 2^{C(n-i,2)} 2C(ni,2)放到一起,再两遍同时除以 n ! n! n!,就可以写出两个EGF F ( x ) = ∑ f [ n ] i ! x i , G ( x ) = ∑ 2 C ( i , 2 ) i ! x i F(x)=\sum \frac{f[n]}{i!}x^i,G(x)=\sum \frac{2^{C(i,2)}}{i!}x^i F(x)=i!f[n]xi,G(x)=i!2C(i,2)xi
这样我们接着处理刚才的式子:
2 C ( n , 2 ) = f [ n ] + ∑ i = 1 n − 1 f [ i ] ∗ n ! i ! ∗ ( n − i ) ! ∗ 2 C ( n − i , 2 ) 2^{C(n,2)}=f[n]+\sum_{i=1}^{n-1}f[i]*\frac{n!}{i!*(n-i)!}*2^{C(n-i,2)} 2C(n,2)=f[n]+i=1n1f[i]i!(ni)!n!2C(ni,2)
2 C ( n , 2 ) = ∑ i = 1 n f [ i ] n ! i ! ∗ ( n − i ) ! ∗ 2 C ( n − i , 2 ) 2^{C(n,2)}=\sum_{i=1}^{n}f[i]\frac{n!}{i!*(n-i)!}*2^{C(n-i,2)} 2C(n,2)=i=1nf[i]i!(ni)!n!2C(ni,2)
G ( x ) = F ( x ) G ( x ) + 1 , 此 处 的 + 1 是 因 为 i = 0 的 时 候 不 满 足 递 推 式 , 所 以 要 加 一 个 常 数 调 整 一 下 G(x)=F(x)G(x)+1,此处的+1是因为i=0的时候不满足递推式,所以要加一个常数调整一下 G(x)=F(x)G(x)+1,+1i=0
F ( x ) = 1 − 1 G ( x ) F(x)=1-\frac{1}{G(x)} F(x)=1G(x)1
多项式求逆求出F(x),就可以解决问题,记得处理好爆int的问题,实在不行就开longlong

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int maxn=1e6+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,fac[maxn],inv[maxn];
inline int ksm(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=1ll*ans*a%mod;
		a=1ll*a*a%mod;b>>=1;
	}
	return ans;
}
int g[maxn],r[maxn];
inline int am(int x){
	if(x>=mod)return x-mod;
	return x;
}
inline void fft(int a[],int lim,int f){
	for(int i=1;i<lim;i++)if(i<r[i])swap(a[i],a[r[i]]);
	for(int i=1;i<lim;i<<=1){
		int wn=ksm(3,(mod-1)/(i<<1));
		if(f==-1)wn=ksm(wn,mod-2);
		for(int j=0;j<lim;j+=(i<<1)){
			int wt=1;
			for(int k=0;k<i;k++,wt=1ll*wt*wn%mod){
				int x=a[j+k],y=1ll*a[j+k+i]*wt%mod;
				a[j+k]=am(x+y);a[j+k+i]=am(x-y+mod);
			}
		}
	}
	if(f==-1){
		int inv=ksm(lim,mod-2);
		for(int i=0;i<lim;i++)a[i]=1ll*a[i]*inv%mod;
	}
}
int lim,l,c[maxn];
int f[maxn];
void getinv(int a[],int b[],int n){
	if(n==1){
		b[0]=ksm(a[0],mod-2);return ;
	}
	getinv(a,b,(n+1)>>1);
	lim=1,l=0;
	while(lim<=(n+n))lim<<=1,l++;
	for(int i=1;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<n;i++)c[i]=a[i];
	for(int i=n;i<lim;i++)c[i]=0;
	fft(c,lim,1);fft(b,lim,1);
	for(int i=0;i<lim;i++)b[i]=1ll*am(2-1ll*c[i]*b[i]%mod+mod)*b[i]%mod;
	fft(b,lim,-1);
	for(int i=n;i<lim;i++)b[i]=0;
}
signed main(){
	n=read();
	fac[0]=inv[0]=1;
	for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
	inv[n]=ksm(fac[n],mod-2);
	for(int i=n-1;i>=1;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
	for(int i=0;i<=n;i++)g[i]=ksm(2,(i*(i-1)/2)%(mod-1));
	for(int i=0;i<=n;i++){
		g[i]=1ll*g[i]*inv[i]%mod;
	}
	getinv(g,f,n+1);
	for(int i=0;i<=n;i++){
		f[i]=am(mod-f[i]);
	}
	f[0]=am(f[0]+1);
	for(int i=0;i<=n;i++)f[i]=1ll*f[i]*fac[i]%mod;
	if(n>=1)puts("1");
	if(n>=2)puts("-1");
	for(int i=3;i<=n;i++)
	printf("%d\n",1ll*fac[i-1]*ksm(2,1ll*i*(i-3)/2%(mod-1))%mod*ksm(f[i],mod-2)%mod);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值