题意
对于一棵 n (1≤n≤1000000) 个点的带标号无根树,设 d[i] 为点 i 的度数。
定义一棵树的方差为数组 d[1..n] 的方差:
令
p=d¯
,(d的平均值)(V为方差)
V=(d1−p)2+(d2−p)2+...+(dn−p)2n
给定 n ,求所有带标号的 n 个点的无根树的方差之和。
你需要将答案对 998244353 取模。
题解
总度数一定等于边数的两倍,所以
p=2(n−1)n
把方差拿来化简
V=(d1−p)2+(d2−p)2+...+(dn−p)2n=d21+d22+..+d2n−2p(d1+d2+...+dn)+np2n=d21+d22+..+d2n−8(n−1)2n+4(n−1)2nn=d21+d22+..+d2n−[2(n−1)]2nn=d21+d22+..+d2nn−p2
百度百科——prufer序列
利用prufer序列可以将每个树转换为一个长度为
n−2
的序列(每个长度为
n−2
的序列也可以转换为一个树)
树总共有
nn−2
个
还有一个性质,一个点的度数d=它在prufer序列中出现次数+1。
现在我们需要求方差
V
的前半截,把所有度数的平方加起来除以n
枚举点在prufer序列中出现的次数0~n-2,则这个点的度数平方之和就为
Cdn−2 :序列中选d个位置填同一个点
n :这些位置可以选择n个点中的任意一个
(d+1)2 :度数的平方
所以总答案就为
ans=(∑d=0n−2Cdn−2×n×(n−1)n−2−d×(d+1)2)/n−p2×nn−2=(∑d=0n−2Cdn−2×(n−1)n−2−d×(d+1)2)−p2×nn−2
代码
#include<cstdio>
const int MAXN=1000005;
const long long MOD=998244353LL;
long long pow_mod(long long a,long long b)
{
long long res=1LL;
while(b)
{
if(b&1LL)
res=(res*a)%MOD;
a=(a*a)%MOD;
b>>=1LL;
}
return res;
}
long long inv(long long x)
{return pow_mod(x,MOD-2);}
long long fac[MAXN],infac[MAXN],npow[MAXN];
void prepare(int n)
{
fac[0]=1LL;
for(int i=1;i<=n;i++)
fac[i]=(fac[i-1]*i)%MOD;
infac[n]=inv(fac[n]);
for(int i=n-1;i>=0;i--)
infac[i]=(infac[i+1]*(i+1))%MOD;
npow[0]=1LL;
for(int i=1;i<=n;i++)
npow[i]=(npow[i-1]*(n-1))%MOD;
}
long long C(long long n,long long m)
{return fac[n]*infac[n-m]%MOD*infac[m]%MOD;}
long long sqr_mod(long long x)
{return x*x%MOD;}
int main()
{
int n;
scanf("%d",&n);
prepare(n);
long long ans=0LL,p;
p=2LL*(n-1)%MOD*inv(n)%MOD;
for(int d=0;d<=n-2;d++)
ans=(ans+C(n-2,d)*sqr_mod(d+1)%MOD*npow[n-2-d]%MOD)%MOD;
ans=(ans+MOD-sqr_mod(p)*pow_mod(n,n-2)%MOD)%MOD;
printf("%lld\n",ans);
return 0;
}