ABC290F Maximum Diameter

好题,但是模拟赛没做出来。

对于长度为 \(n\) 的正整数序列 \(X\),若一棵 \(n\) 个节点的树满足编号为 \(i\) 的点度数为 \(x_i\),则称这棵树是序列 \(X\) 的生成树。定义 \(f(X)\) 为序列 \(X\) 所有生成树的直径最大值,若序列 \(X\) 不存在生成树,则 \(f(X)=0\)

求所有长度为 \(n\) 的正整数序列 \(X\) 对应的 \(f(X)\) 之和,对 \(998244353\) 取模。

\(T \leq 2 \times 10^5\) 组数据,\(2 \leq n \leq 10^6\)

首先 \(\sum\limits_{i=1}^{n} x_i = 2n-2\) 是显然的,由此可知存在生成树的正整数序列 \(X\) 是有限的。接下来可以得到结论:

对于满足 \(\sum\limits_{i=1}^{n} x_i = 2n-2\) 的正整数序列 \(X\),记 \(X\)\(1\) 的个数为 \(t\),则 \(f(X)=n-t+1\)

首先我们可以发现序列 \(t \geq 2\),即若 \(X\) 存在生成树,则生成树的叶子节点个数 \(\geq 2\)。不妨钦定叶子节点 \(u\)\(v\) 作为直径的两个端点,我们有如下构造:

  • \(u\)\(v\) 之间连接所有非叶子节点,将剩余的所有叶子节点连接到非叶子节点上

容易发现在 \(\sum\limits_{i=1}^{n} x_i = 2n-2\)\(t \geq 2\) 的条件下,这样的构造一定存在。在这种构造中,生成树的直径包含了 \(u\)\(v\) 两个叶子节点以及所有非叶子节点。又由于直径最多包含两个叶子节点,所以这种构造一定是最优的,直径长度 \(f(X)=n-t+1\) 也是显然的。

接下来就可以抛开树的限制了。我们不妨枚举 \(t\),此时在序列 \(X\)\(n\) 个下标中选择 \(t\) 个填入 \(1\),此时的方案总数为 \(\dbinom{n}{t}\),接下来剩余 \(n-t\) 个下标,其中每个下标对应的数都满足 \(x_i \geq 2\),且总和为 \(2n-2-t\)。考虑插板法,容易发现此时的方案总数为 \(\dbinom{n-2-1}{n-t-1}=\dbinom{n-3}{n-t-1}\),每个方案的贡献为 \(n-t+1\),容易写出计算式:

\[\sum\limits_{t} \dbinom{n}{t}\dbinom{n-3}{n-t-1}(n-t+1) \]

此时已经可以通过预处理组合数实现单次 \(O(n)\) 的计算了,但这仍然不够。

考虑将 \(n-t+1\) 拆开,得到原式等于下面的式子:

\[\sum\limits_{t} \dbinom{n}{t}\dbinom{n-3}{n-t-1}(n-t-1)+2\sum\limits_{t} \dbinom{n}{t}\dbinom{n-3}{n-t-1} \]

对于左半部分,容易化为下面的形式:

\[(n-3)\sum\limits_{t} \dbinom{n}{t} \dbinom{n-4}{n-t-2} \]

使用范德蒙德卷积化为:

\[(n-3) \dbinom{2n-4}{n-2} \]

对于右半部分,使用范德蒙德卷积:

\[2\dbinom{2n-3}{n-1} \]

于是我们得到了最终的答案:

\[(n-3)\dbinom{2n-4}{n-2} + 2\dbinom{2n-3}{n-1} \]

预处理组合数即可,复杂度 \(O(n+T)\)

#include<iostream>
#include<cstdio>
using namespace std;
const long long mod=998244353;
const int N=1000000;
long long fact[2*N+10],invfact[2*N+10];
long long inv(long long num){
	long long pre=mod-2,ans=1;
	while(pre){
		if(pre&1){
			ans=ans*num;
			ans%=mod;
		}
		num=num*num;
		num%=mod;
		pre>>=1; 
	}
	return ans;
}
void init(){
	fact[0]=invfact[0]=1;
	for(int i=1;i<=2*N;i++){
		fact[i]=fact[i-1]*i%mod;
		invfact[i]=invfact[i-1]*fact[i]%mod;
	}
	long long pre_inv=inv(invfact[2*N]);
	for(int i=2*N;i>=1;i--){
		invfact[i]=invfact[i-1]*pre_inv%mod;
		pre_inv=pre_inv*fact[i]%mod;
	}
}
int main(){
	int T;
	scanf("%d",&T);
	init();
	while(T--){
		int n;
		scanf("%d",&n);
		long long ans=0;
		ans+=(n-3)*fact[2*n-4]%mod*invfact[n-2]%mod*invfact[n-2]%mod;
		ans=(ans%mod+mod)%mod;
		ans+=2*fact[2*n-3]%mod*invfact[n-1]%mod*invfact[n-2]%mod;
		ans=(ans%mod+mod)%mod;
		printf("%lld\n",ans); 
	} 
	return 0;
}
posted @ 2025-12-16 14:18  Oken喵~  阅读(6)  评论(0)    收藏  举报