【hihoCoder1511】树的方差

题意

对于一棵 n (1≤n≤1000000) 个点的带标号无根树,设 d[i] 为点 i 的度数。
定义一棵树的方差为数组 d[1..n] 的方差:
p=d¯ ,(d的平均值)(V为方差)

V=(d1p)2+(d2p)2+...+(dnp)2n

给定 n ,求所有带标号的 n 个点的无根树的方差之和。
你需要将答案对 998244353 取模。

题解

总度数一定等于边数的两倍,所以

p=2(n1)n

把方差拿来化简
V=(d1p)2+(d2p)2+...+(dnp)2n=d21+d22+..+d2n2p(d1+d2+...+dn)+np2n=d21+d22+..+d2n8(n1)2n+4(n1)2nn=d21+d22+..+d2n[2(n1)]2nn=d21+d22+..+d2nnp2

百度百科——prufer序列
利用prufer序列可以将每个树转换为一个长度为 n2 的序列(每个长度为 n2 的序列也可以转换为一个树)
树总共有 nn2
还有一个性质,一个点的度数d=它在prufer序列中出现次数+1。

现在我们需要求方差 V 的前半截,把所有度数的平方加起来除以n
枚举点在prufer序列中出现的次数0~n-2,则这个点的度数平方之和就为

Cdn2×n×(n1)n2d×(d+1)2

Cdn2 :序列中选d个位置填同一个点
n :这些位置可以选择n个点中的任意一个
(n1)n2d : 剩余(n-2-d)个位置填其它点的方案数
(d+1)2 :度数的平方

所以总答案就为

ans=(d=0n2Cdn2×n×(n1)n2d×(d+1)2)/np2×nn2=(d=0n2Cdn2×(n1)n2d×(d+1)2)p2×nn2

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/can919/article/details/79112466