Valuable Forests【prufer序列】

题意

定义一棵无根树 \(T\) 的价值为:\(\sum_{u\in V(T)}{(d(u))^2}\),其中 \(V(T)\)\(T\) 的所有点组成的点集,\(d(u)\) 是点 \(u\) 的度。定义森林的价值为所有由它生成的树的价值之和。现在,让你求出由 \(N\) 个编号的点组成的所有森林的价值之和,结果对 \(M\) 取模。

\(1\leq T \leq 5000,1\leq M \leq 2^{30} 且为素数,1\leq N \leq 5000\)

题目链接:https://ac.nowcoder.com/acm/contest/5672/I

分析

本需要用到 \(\text{prufer}\) 序列的有关性质。

定义如下数组:

\(f(n)\)\(n\) 个点的森林个数

\(st(n)\)\(n\) 个点的完全图,可以构成 \(n^{n-2}\) 个不同的树(根据序列的性质)

在第 \(n\) 个点加入时,可以选择 \(i\) 个点与它组成一棵树,有:

\[f(n)=\sum_{i=0}^{n-1}{C_{n-1}^{i}·st(i+1)·f(n-i-1)} \]

\(n\) 个点能形成的所有无根树的权值和为 \(A_n\)

枚举每一个点 \(i\),再枚举这个点的度数 \(j\)。第 \(i\) 个点的度数为 \(j\) 的贡献 \(=\) \(j^2\) 与序列中有且仅有 \(j-1\)\(i\) 的方案数之积。

\[A(n)=\sum_{i=1}^{n}{\sum_{j=1}^{n-1}{j^2·C_{n-2}^{j-1}(n-1)^{n-j-1}}} \]

\(i\) 省略,可得:

\[A(n)=n\sum_{j=1}^{n-1}{j^2·C_{n-2}^{j-1}(n-1)^{n-j-1}} \]

最后,假设 \(n\) 个点的形成的森林的权值和为 \(F_n\),类似求 \(f_n\) 的方法,将第 \(n\) 个点加入时,选择 \(i\) 个点和它构成一棵树,可得:

\[F(n)=\sum_{i=0}^{n-1}{C_{n-1}^{i}(st(i+1)·F(n-i-1)+f(n-i-1)·A(i+1))} \]

选择 \(i\) 个点和第 \(n\) 个点形成一个有 \(i+1\) 个点的树,与之相对应,其他的点能形成 \(f(n-i-1)\) 种森林,对于每一种形成方式,都能得到 \(A(i+1)\) 的权值贡献,因此他们需要相乘,前面的式子同理。

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=5050;
int f[N],F[N],A[N],st[N],c[N][N];
int M;
ll power(ll a,ll b)
{
    ll res=1;
    a%=M;
    while(b)
    {
        if(b&1) res=res*a%M;
        a=a*a%M;
        b>>=1;
    }
    return res;
}
void init()
{
    int maxn=5000;
    for(int i=0;i<=maxn;i++)
    {
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%M;
    }
    st[0]=st[1]=1;
    for(int i=2;i<=maxn;i++)
        st[i]=power(i,i-2);
    f[0]=f[1]=1;
    for(int i=2;i<=maxn;i++)
    {
        for(int j=0;j<i;j++)
            f[i]=(f[i]+1LL*c[i-1][j]*st[j+1]%M*f[i-j-1]%M)%M;
    }
    A[1]=0;
    for(int i=2;i<=maxn;i++)
    {
        int res=0;
        int inv=power(i-1,M-2);
        int tp=power(i-1,i-1);
        for(int j=1;j<i;j++)
        {
            tp=1LL*tp*inv%M;
            res=(res+1LL*j*j%M*c[i-2][j-1]%M*tp%M)%M;
        }
        A[i]=1LL*i*res%M;
    }
    for(int i=1;i<=maxn;i++)
    {
        F[i]=0;
        for(int j=0;j<i;j++)
            F[i]=(F[i]+1LL*c[i-1][j]*(1LL*st[j+1]*F[i-j-1]%M+1LL*f[i-j-1]*A[j+1]%M)%M)%M;
    }
}
int main()
{
    int T,n;
    scanf("%d%d",&T,&M);
    init();
    while(T--)
    {
        scanf("%d",&n);
        printf("%d\n",F[n]);
    }
    return 0;
}

posted @ 2020-09-07 09:17  xzx9  阅读(119)  评论(0编辑  收藏  举报