CF1667E Centroid Probabilities
给定奇数 \(n\)。有一棵 \(n\) 个点的树,点 \(i\) 的父亲 \(p_i \in [1,i-1]\)。
对于所有 \(i \in [1,n]\),求出满足条件的所有树中,重心为点 \(i\) 的树的数量。
\(3 \leq n < 2 \times 10^5\)。
我们令 \(m = \dfrac{n+1}{2}\),则点 \(i\) 为重心的必要条件是 \(\text{size}_i \geq m\)。
我们不妨先求出 \(f_i\) 表示 \(\text{size}_i \geq m\) 的方案数。
我们不妨考虑枚举 \(\text{size}_i = j\),考虑如何计数。
首先,我们需要选择 \(j\) 个点作为子树,方案数 \(\dbinom{n-i}{j-1}\)。
这 \(j\) 个点构成一棵树,方案数为 \((j-1)!\)。
不在 \(i\) 子树内的点父节点只能在内部产生,方案数为 \((n-j-1)!\)。
\(i\) 的父亲有 \(i-1\) 种选法,即编号比 \(i\) 小的节点均可以选。
由上面的分析,可以得到:
接下来对其化简:
接下来考虑如何利用 \(f_i\) 求出答案 \(g_i\)。
在 \(\text{size}_i \geq m\) 的必要条件下,可能会发生重心在 \(i\) 子树内的情况。
进行容斥,对于 \(j \in [i+1,n]\),考虑 \(j\) 在 \(i\) 子树内的概率。
显然,顺着父亲往上跳,第一个到达的 \([1,i]\) 之间的节点是等概率随机的。
只有到达 \(i\),\(j\) 才能在 \(i\) 子树内。即 \(j\) 在 \(i\) 子树内概率为 \(\dfrac{1}{i}\)。
也就是说,\(g_i = f_i - \dfrac{\sum\limits_{j=i+1}^{n} g_j}{i}\)。
我们进行递推即可,时间复杂度 \(O(n)\),为了方便我写的是 \(O(n \log n)\)。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long mod=998244353;
long long f[200010],g[200010],fact[200010],inv_fact[200010];
long long inv(long long num){
long long pre=mod-2,ans=1;
while(pre){
if(pre&1){
ans=ans*num%mod;
}
num=num*num%mod;
pre>>=1;
}
return ans;
}
int main(){
int n;
scanf("%d",&n);
fact[0]=inv_fact[0]=1;
for(int i=1;i<=n;i++){
fact[i]=fact[i-1]*i%mod;
}
int m=(n+1)/2;
for(int i=1;i<=n;i++){
if(n-m>=i-1){
f[i]=fact[n-i]*fact[n-m]%mod*inv(fact[n-m-i+1])%mod;
}
}
long long sum=0;
for(int i=n;i>=1;i--){
g[i]=f[i]-sum*inv(i)%mod;
g[i]=(g[i]%mod+mod)%mod;
sum+=g[i];
sum%=mod;
}
for(int i=1;i<=n;i++){
printf("%lld ",g[i]);
}
return 0;
}

浙公网安备 33010602011771号