luogu P4233 射命丸文的笔记

20 篇文章 0 订阅
18 篇文章 0 订阅

背景:

暑假集训七月份最后一天,写写原来没有写过的 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(n1)条边,去掉组成环的 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(n1)n=(n1)!22n(n1)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(n1)
枚举拓扑序最小的强连通分量大小为 i i i,任意选出 i i i个点的方案为 C n i C_{n}^{i} Cni,而其它的点向该强连通分量连边的方向是固定的,剩下 n − i n-i ni个点的边任意连,所以有:
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=1n1Cnifigni

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=1n1i!(ni)!n!figni

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=1n1i!fi(ni)!gni

里面是一个分治 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=GG1

F = 1 − 1 G F=1-\frac{1}{G} F=1G1

多项式求逆即可。


最后的期望就等于 竞 赛 图 的 哈 密 顿 回 路 的 总 数 值 得 记 录 的 竞 赛 图 的 总 数 \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);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值