luogu P4233 射命丸文的笔记

只愿长相守 提交于 2020-02-11 00:02:08

题目

题意:给出 \(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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!