P9535 [YsOI2023] 连通图计数

神\se
考虑 \(m=n-1\),唯一的无向连通图是一棵树,给的 \(a_i\) 刚好等于每个点的度数,利用 Prüfer 序列的结论,答案为 \(\binom{n-2}{a_1-1,a_2-1,...,a_n-1}\),记 \(r_1=\frac{1}{a_1!a_2!...a_n!}\),答案即 \(r_1\times(n-2)!\)

考虑 \(m=n\),无向连通图是一颗基环树,考虑建成圆方树,那么有且仅有一个方点度数大于等于 \(2\)。考虑求出方点度数 \(d=2n-\sum_{i=1}^na_i\)

证明:考虑非环上的边一定为割边,这样子会在两个端点的 \(a\) 分别记录一次连通块贡献,相当于每条边贡献两次。环上的点,每个点删去后,会贡献一次,相当于环上的边每条边贡献一次。

求出 \(d\) 之后利用上面的结论,可以得知改圆方树的方案数为 \(\frac{r_1\times(n-1)!}{(d-1) !}\)。考虑求的是奇环树的方案数,这和圆上点的顺序有关,而我们的方案对于环上的点是无序的,乘上圆排列的方案数,即 \(\frac{1}{2}(d-1)!\)。所以得出来的方案为 \(\frac{1}{2}r_1\times(n-1)!\)

考虑 \(m=n+1\)。需要分类讨论。

第一种,只有一个方点度数超过 \(2\)。这个方点的度数为 \(d\),我们计算出树的方案数,再转化成图的方案数。相当于再乘上 \(G(d)\)\(G(n)\) 表示 \(n\) 个点,\(n+1\)条边形成 \(1\) 个点双联通的方案数。

考虑求 \(G(n)\),这样的点双恰好有两个点度数为 \(3\),其余点度数为 \(2\)。相当于两个点由三条没有编号的链连接起来,先选出两个点 \(\binom{n}{2}\)。然后算出其余的点分到三条链的排列数。相当于先随机排列,然后利用插板法分到三条链,这个组合数是 \(\binom{n-2+2}{2}\),还要减去 \(3\),因为最多一条链是没有点的。所以这里是 \((n-2)!(\binom{n}{2}-3)\)。又因为三条链是无序的。所以 $$G(n)=\binom{n}{2}\frac{(n-2)!(\binom{n}{2}-3)}{3!}$$

这一部分贡献是 \(\frac{r1\times(n-1)!\times G_1(d)}{(d-1)!}\)
第二种,有两个方点度数超过 \(2\),相当于一个有两个环的仙人掌。
我们可以枚举第一个环的点数,然后算出总的答案,最后除以 \(2\)。因为两个环是无序的。
\(F'(x,y)\) 表示这个对应数,因为 Prüfer 的计算中,\(\frac{1}{x!}\) 会和圆排列抵消掉。所以答案和 \(x,y\) 无关,只和 \(d\) 有关,这里的 \((x,y)\) 一共有 \(d-3\) 对。我们可以记成 \(\frac{d-3}{2}F(d)\)

考虑算出 \(F(d)\),根据上面计算,树的贡献为 \(\frac{1}{4}r_1n!\)。但是可能会出现两个方点连在一起的情况,我们要减去。连在一起我们可以把两个方点看做一个点,这样子是 \(\frac{r_1(n-1)!}{(d-1)!}\)。我们再把这 \(d\) 个点分成两个弧,这里圆排列是 \(\frac{d!}{4}\),(和普通圆排列不一样,\(1,2|3\)\(3,1|2\)不一样,但是 \(2,1|3,4\)\(1,2|4,3\)\(1,2|3,4\) 等价。

所以总的是 \(\frac{1}{4}(r_1n!-r_1(n-1)!d)\)

最后,这一部分的贡献就是 \(\frac{d-3}{8}(r_1n!-r_1(n-1)!d)\)

code
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=2e5+5,inf=1e9;
const int Mod=998244353;
int n,m,a[N]; LL ans,pw[N],pwn[N];
LL Pow(LL x,LL k){
	LL y=1;
	while(k){
		if(k & 1) y=y*x%Mod;
		k>>=1; x=x*x%Mod;
	}
	return y;
}
LL C(int n,int m){
	if(n<m) return 0;
	return pw[n]*pwn[m]%Mod*pwn[n-m]%Mod;
}
LL Fds(int n){
	LL x=C(n,2);
	return x*pw[n-2]%Mod*(x-3+Mod)%Mod*pwn[3]%Mod;
}
int main(){
	pw[0]=1; for(int i=1;i<N;i++) pw[i]=pw[i-1]*i%Mod;
	pwn[N-1]=Pow(pw[N-1],Mod-2); for(int i=N-2;i>=0;i--) pwn[i]=pwn[i+1]*(i+1)%Mod;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	LL r1=1; for(int i=1;i<=n;i++) (r1*=pwn[a[i]-1])%=Mod;
	if(m==n-1) ans=pw[n-2]*r1%Mod;
	else {
		int d=2*n;
		for(int i=1;i<=n;i++) d-=a[i];
		if(m==n) ans=r1*pw[n-1]%Mod*pwn[2]%Mod;
		else if(d>3){
			ans=r1*pw[n-1]%Mod*pwn[d-1]%Mod*Fds(d)%Mod;
			LL t1=r1*pw[n],t2=r1*pw[n-1]%Mod;
			t1=(t1-t2*d%Mod+Mod)%Mod;
			t1=t1*(d-3)%Mod*Pow(8,Mod-2)%Mod;
			(ans+=t1)%=Mod;
		}
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2023-08-20 23:16  Shui_dream  阅读(84)  评论(0)    收藏  举报