题意:给出 (n),在 有哈密顿回路的 (n) 个点的竞赛图中等概率选出一个,求哈密顿回路个数的期望。
答案就是 哈密顿回路的总条数 除以 有哈密顿回路的竞赛图的个数。
哈密顿回路的总条数是很好求的,对每个环算贡献,算出 ((n-1)!2^{C_n^2-n})。
有哈密顿回路的竞赛图的个数,这个有点难办。
结论:一个竞赛图有哈密顿回路 的充要条件是 它强连通。
必要性显然,简单证证充分性。(如果假了或者有更好的证法请告诉我qwq)
归纳证明。对于点数很小的情况,容易验证是对的。现在假设已经证明了点数在 (n-1) 以内时结论是对的。
假设去掉 (n) 号点,这时图仍是竞赛图但不一定强连通,此时图中有若干个强连通分量。
把强连通分量缩点变成拓扑图后,显然拓扑序是唯一的,设为 ((S_1 ightarrow S_2 ightarrow cdots ightarrow S_k)),其中 (S_i)为一个强连通分量。
因为要强连通,(n) 和 (S_1) 之间有至少一条边方向是 ((n ightarrow S_1)),而 (n) 和 (S_k) 之间有至少一条边方向是 ((S_k ightarrow n))。
由于 (S_i) 的点数 (|S_i|) 小于 (n) ,所以我们已经证明了它有哈密顿回路。
考虑从每个强连通分量的哈密顿回路中选出一条边 ((u ightarrow v)) 删掉,然后让 (v) 成为这个强连通分量的入点,(u) 成为这个强连通分量的出点。
对于一个强连通分量只有一个点的情况,这个点既是入点又是出点。
我们按照 ((n ightarrow S_1 ightarrow S_2 ightarrow cdots ightarrow S_k ightarrow n)) 的顺序,上一个分量的出点向这一个分量的入点连上边,即构造出一个哈密顿回路。
于是证完了。
所以只要求强连通竞赛图的个数。
考虑容斥,用 (n) 个点竞赛图总数减去不连通的个数。
不连通的个数又怎么算?我们枚举缩点后拓扑序最靠前的那个强连通分量的大小,则得出式子:
(f_n=2^{C_n^2}-sum_{i=1}^{n-1} C_n^{i}f_i 2^{C_{n-i}^{2}})
于是得到一个平方做法。
移项一下,容易得到 (2^{C_n^2}=sum_{i=1}^{n} C_n^{i}f_i 2^{C_{n-i}^{2}})
构造 (F(x)=sum_i frac{2^{C_i^2}}{i!}x^i,G(x)=sum_i frac{f_i}{i!}x^i),由上面式子可以得到 (F(x)=F(x)G(x)+1)。
(G(x)=frac{F(x)-1}{F(x)})
多项式求逆解决。
(n) 特别小的情况要判一判。
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
const int K=18,M=998244353,G=3;
inline int Pow(int a,ll m){int s=1;for(;m;m>>=1)m&1?s=(ll)s*a%M:0,a=(ll)a*a%M;return s;}
namespace poly{
int rev[1<<K],t0[1<<K];
inline void Init(int K){
for(int i=0;i<(1<<K);i++)rev[i]=rev[i>>1]>>1|(i&1)*(1<<K-1);
}
inline void Ntt(int*a,int f,int K){
int s,omega,tmp0,tmp1;
for(int i=0;i<(1<<K);i++)if(i<rev[i])std::swap(a[i],a[rev[i]]);
for(int k=1;k<(1<<K);k<<=1){
omega=Pow(G,f>0?M/2/k:M-1-M/2/k);
for(int i=0;i<(1<<K);i+=k+k){
s=1;
for(int j=0;j<k;j++,s=(ll)s*omega%M)
tmp0=a[i+j],tmp1=(ll)a[i+k+j]*s%M,
a[i+j]=(tmp0+tmp1)%M,a[i+k+j]=(tmp0-tmp1+M)%M;
}
}
if(f<0){
tmp0=Pow(1<<K,M-2);
for(int i=0;i<(1<<K);i++)a[i]=(ll)a[i]*tmp0%M;
}
}
inline void Inv(int*c,int*a){
c[0]=Pow(a[0],M-2);
for(int i=1;i<(1<<K);i++)c[i]=0;
for(int j=0;j<K;j++){//
Init(j+1);
memcpy(t0,a,sizeof(int)*(1<<j));
memset(t0+(1<<j),0,sizeof(int)*(1<<j));
Ntt(c,1,j+1),Ntt(t0,1,j+1);
for(int i=0;i<(1<<K);i++)c[i]=(2ll*c[i]-(ll)c[i]*c[i]%M*t0[i]%M+M)%M;
Ntt(c,-1,j+1);
memset(c+(1<<j),0,sizeof(int)*(1<<j));
}
}
inline void Mult(int*c,int*a,int*b){
Init(K);
Ntt(a,1,K),Ntt(b,1,K);
for(int i=0;i<(1<<K);i++)c[i]=(ll)a[i]*b[i]%M;
Ntt(c,-1,K);
}
}
int t[1<<K],a[1<<K],g[1<<K],f[1<<K],invf[1<<K],k,n;
inline int C(int n,int m){return(ll)f[n]*invf[m]%M*invf[n-m]%M;}
int main(){
f[0]=1;
for(int i=1;i<(1<<K);i++)f[i]=(ll)f[i-1]*i%M;
invf[(1<<K)-1]=Pow(f[(1<<K)-1],M-2);
for(int i=(1<<K)-1;i;i--)invf[i-1]=(ll)invf[i]*i%M;
for(int i=0;i<(1<<K-1);i++)a[i]=(ll)Pow(2,(ll)i*(i-1)/2)*invf[i]%M;
poly::Inv(t,a);
for(int i=0;i<(1<<K-1);i++)g[i]=!i?1:(ll)Pow(2,(ll)i*(i-1)/2+1)*invf[i]%M;
memset(g+(1<<K-1),0,sizeof(int)*(1<<K-1));
memset(t+(1<<K-1),0,sizeof(int)*(1<<K-1));
poly::Mult(g,t,g);
memset(g+(1<<K-1),0,sizeof(int)*(1<<K-1));
for(int i=0;i<(1<<K);i++)g[i]=(ll)g[i]*f[i]%M;
scanf("%d",&n);
for(int i=1;i<=n;i++){
if(i==1){ puts("1");continue;}
if(i==2){puts("-1");continue;}
printf("%lld
",(ll)f[i-1]*Pow(2,(ll)i*(i-1)/2-i)%M*Pow(g[i],M-2)%M);
}
return 0;
}