背景:
暑假集训七月份最后一天,写写原来没有写过的
blog
\text{blog}
blog吧。
题目传送门:
https://www.luogu.org/problemnew/show/P4233
题意:
求一个有
n
n
n个点的竞赛图的哈密顿回路的数量的期望。
竞赛图:指任意两个顶点间恰有一条有向边的有向图;
哈密顿回路:指除起点和终点外经过所有顶点恰好一次且起点和终点相同的路径。
思路:
Part1 \text{Part1} Part1:竞赛图的哈密顿回路的总数
当前有
n
n
n个点。
我们从起点出发,经过所有点回到起点,组成一个
1
1
1至
n
n
n的排列,方案数为
n
!
n!
n!;而路径是一个环,所以每条路径被计算了
n
n
n次。
一共有
n
(
n
−
1
)
2
\frac{n(n-1)}{2}
2n(n−1)条边,去掉组成环的
n
n
n条,剩余的边的方向任意(两个方向,入或出)。
因此总数为
n
!
n
2
n
(
n
−
1
)
2
−
n
=
(
n
−
1
)
!
2
n
(
n
−
1
)
2
−
n
\frac{n!}{n}2^{\frac{n(n-1)}{2}-n}=(n-1)!2^{\frac{n(n-1)}{2}-n}
nn!22n(n−1)−n=(n−1)!22n(n−1)−n。
Part2 \text{Part2} Part2:值得记录的竞赛图的总数
设
f
n
f_n
fn表示值得记录的竞赛图的总数。
设
g
n
g_n
gn表示竞赛图的总数。
显然
g
n
=
2
n
(
n
−
1
)
2
g_n=2^{\frac{n(n-1)}{2}}
gn=22n(n−1)。
枚举拓扑序最小的强连通分量大小为
i
i
i,任意选出
i
i
i个点的方案为
C
n
i
C_{n}^{i}
Cni,而其它的点向该强连通分量连边的方向是固定的,剩下
n
−
i
n-i
n−i个点的边任意连,所以有:
g
n
=
∑
i
=
1
n
−
1
C
n
i
f
i
g
n
−
i
g_n=\sum_{i=1}^{n-1}C_{n}^{i}f_ig_{n-i}
gn=i=1∑n−1Cnifign−i
g n = ∑ i = 1 n − 1 n ! i ! ( n − i ) ! f i g n − i g_n=\sum_{i=1}^{n-1}\frac{n!}{i!(n-i)!}f_ig_{n-i} gn=i=1∑n−1i!(n−i)!n!fign−i
g n n ! = ∑ i = 1 n − 1 f i i ! g n − i ( n − i ) ! \frac{g_n}{n!}=\sum_{i=1}^{n-1}\frac{f_i}{i!}\frac{g_{n-i}}{(n-i)!} n!gn=i=1∑n−1i!fi(n−i)!gn−i
里面是一个分治
NTT
\text{NTT}
NTT的形式(然而我一辈子都不会打分治
NTT
\text{NTT}
NTT的)。
考虑生成函数的表示:
G
=
F
G
+
g
0
G=FG+g_0
G=FG+g0
其中
g
0
=
1
g_0=1
g0=1。
F
=
G
−
1
G
F=\frac{G-1}{G}
F=GG−1
F = 1 − 1 G F=1-\frac{1}{G} F=1−G1
多项式求逆即可。
最后的期望就等于
竞
赛
图
的
哈
密
顿
回
路
的
总
数
值
得
记
录
的
竞
赛
图
的
总
数
\frac{竞赛图的哈密顿回路的总数}{值得记录的竞赛图的总数}
值得记录的竞赛图的总数竞赛图的哈密顿回路的总数。
即可。
注意快速幂
k
k
k要开
long long
\text{long long}
long long。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const int mod=998244353,G=3,inv_G=332748118;
using namespace std;
int a[1000010],b[1000010],f[1000010],g[1000010],fac[1000010],Inv[1000010];
int limit,n,l,r[1000010];
int dg(int x,LL k)
{
if(!k) return 1;
int op=dg(x,k>>1);
if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int inv(int x)
{
return dg(x,mod-2);
}
void init(int n)
{
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,l++;
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1))
{
int w=1;
for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
{
int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
}
void dft(int *f,int n,int limit)
{
NTT(f,limit,-1);
int INV=inv(limit);
for(int i=0;i<n;i++)
f[i]=(LL)f[i]*INV%mod;
}
void poly_inv(int *f,int *g,int n)
{
if(n==1)
{
g[0]=inv(f[0]);
return;
}
poly_inv(f,g,(n+1)>>1);
init(n);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n;i++)
a[i]=f[i],b[i]=g[i];
NTT(a,limit,1),NTT(b,limit,1);
for(int i=0;i<limit;i++)
b[i]=(LL)b[i]*((2ll-(LL)a[i]*b[i]%mod+mod)%mod)%mod;
dft(b,n,limit);
for(int i=0;i<n;i++)
g[i]=b[i];
}
void INIT(int n)
{
fac[0]=fac[1]=1;
Inv[0]=Inv[1]=1;
for(int i=2;i<=n;i++)
{
fac[i]=(LL)fac[i-1]*i%mod;
Inv[i]=((LL)mod-mod/i)*Inv[mod%i]%mod;
}
for(int i=1;i<=n;i++)
Inv[i]=(LL)Inv[i-1]*Inv[i]%mod;
}
int main()
{
scanf("%d",&n);
INIT(n);
g[0]=1;
for(int i=1;i<=n;i++)
g[i]=(LL)dg(2,(LL)i*(i-1)/2)*Inv[i]%mod;
poly_inv(g,f,n+1);
for(int i=0;i<=n;i++)
f[i]=(-f[i]+mod)%mod;
f[0]=(f[0]+1)%mod;
for(int i=0;i<=n;i++)
f[i]=(LL)f[i]*fac[i]%mod;
if(n>=1) printf("1\n");
if(n>=2) printf("-1\n");
for(int i=3;i<=n;i++)
printf("%d\n",(LL)fac[i-1]*dg(2,(LL)i*(i-1)/2-i)%mod*inv(f[i])%mod);
}