题意:给出 \(n\),在 有哈密顿回路的 \(n\) 个点的竞赛图中等概率选出一个,求哈密顿回路个数的期望。
答案就是 哈密顿回路的总条数 除以 有哈密顿回路的竞赛图的个数。
哈密顿回路的总条数是很好求的,对每个环算贡献,算出 \((n-1)!2^{C_n^2-n}\)。
有哈密顿回路的竞赛图的个数,这个有点难办。
结论:一个竞赛图有哈密顿回路 的充要条件是 它强连通。
必要性显然,简单证证充分性。(如果假了或者有更好的证法请告诉我qwq)
归纳证明。对于点数很小的情况,容易验证是对的。现在假设已经证明了点数在 \(n-1\) 以内时结论是对的。
假设去掉 \(n\) 号点,这时图仍是竞赛图但不一定强连通,此时图中有若干个强连通分量。
把强连通分量缩点变成拓扑图后,显然拓扑序是唯一的,设为 \((S_1\rightarrow S_2\rightarrow \cdots\rightarrow S_k)\),其中 \(S_i\)为一个强连通分量。
因为要强连通,\(n\) 和 \(S_1\) 之间有至少一条边方向是 \((n\rightarrow S_1)\),而 \(n\) 和 \(S_k\) 之间有至少一条边方向是 \((S_k\rightarrow n)\)。
由于 \(S_i\) 的点数 \(|S_i|\) 小于 \(n\) ,所以我们已经证明了它有哈密顿回路。
考虑从每个强连通分量的哈密顿回路中选出一条边 \((u\rightarrow v)\) 删掉,然后让 \(v\) 成为这个强连通分量的入点,\(u\) 成为这个强连通分量的出点。
对于一个强连通分量只有一个点的情况,这个点既是入点又是出点。
我们按照 \((n\rightarrow S_1\rightarrow S_2\rightarrow \cdots\rightarrow S_k\rightarrow 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\n",(ll)f[i-1]*Pow(2,(ll)i*(i-1)/2-i)%M*Pow(g[i],M-2)%M); } return 0; }
来源:https://www.cnblogs.com/Camp-Nou/p/12293518.html