• luogu P4233 射命丸文的笔记


    题目

    题意:给出 (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;
    }
    
  • 相关阅读:
    php-instanceof运算符
    windows10-seaslog安装笔记
    [类和对象]1 封装 调用成员函数
    [C++] 拓展属性
    [C++] 引用详解
    [C++] Const详解
    ROS 常用
    win10 ubuntu16双系统安装教程
    [0] OpenCV_Notes
    Ubuntu16.04安装openCV的问题集合
  • 原文地址:https://www.cnblogs.com/Camp-Nou/p/12293518.html
Copyright © 2020-2023  润新知