牛客网Valuable Forests

题目描述:

自行解决。

分析:

一个森林内部节点的度数平方和等于 2 * (长度为 2 的路径数+长度为 3 的路径数)。

n 个点的带标号树个数为n n−2 • 我们可以通过一个 n 2 的做法得到 n 个点的带标号森林个数。

我们分别统计长度为 2 的路径的贡献, 相当于从 n 个点里面挑出 2 个点, 设这两个点所在树大小为 j, 那么就需要从剩下 n – 2 个点里面挑出 j – 2 个点, 然后挑出的这 j 个点构成一棵树, 剩下的 n-j 个点 构成森林。

j 个点构成的树需要以那两个点为根(相当于把这两个点看成一个整体), 用 prufer 序列的处 理可以知道方案数是2𝑗 𝑗−3

长度为 3 的路径同理, j 个点以特定 3 个点为根的方案数是 3𝑗 𝑗−4

考虑只有一对(w, b)要怎么处理。不妨假设w> b, 先求出肯定在白色连通块里的点。

可以发现,如果存在这样的点集的话,把考虑只有一对(w, b)要怎么处理。

不妨假设w> b, 先求出肯定在白色连通块里的点。可以发现,如果存在这样的点集的话,把考虑只有一对(w, b)要怎么处理。

不妨假设w> b, 先求出肯定在白色连通块里的点。

可以发现,如果存在这样的点集的话,把

这些点删掉,剩下连通块大小大于等于b的个数就是答案; 否则答案要么是1,要么是2。

并且答案为1当且仅当存在一个节点,使得中有两颗子树的SIZE 2 w,并存在另一棵子树的SIZE > b。

根据上面的结论,我们可以很方便的解决这个题。

考虑固定一个w, -个节点x- -定在白色连通块里,当且仅当节点xc的所有子树大小都小于w。

当w = n的时候,所有点都在白色连通块里。随着w逐渐变小,白色连通块上点个数也逐渐减少,剩下的连通

块们会逐渐合并,显然用一个并查集就可以维护这些连通块的大小。

这就做出来了,就这?


添上灵魂

#include<bits/stdc++.h>
using namespace std;
int n;
int t,mod;
int num[100010];
int a[100010],st[5010];
int C[5010][5010],A[5010],F[5010],f[5010];
int fastpow(int x,int a)
{
    int ans=1;
    for (; a; x=1ll*x*x%mod,a>>=1)
        if (a&1) ans=1ll*ans*x%mod;
    return ans;
}
int main()
{
    scanf("%d%d",&t,&mod);
    C[0][0]=1;
    for (int i=1; i<=5000; i++)
    {
        C[0][i]=1;
        for (int j=1; j<=i; j++)
            C[j][i]=(1ll*C[j][i-1]+C[j-1][i-1])%mod;
    }
    st[0]=1;
    st[1]=1;
    for (int N=1; N<=5000; N++)
    {
        for (int d=1; d<=N-1; d++)
            A[N]=(1ll*d*d*C[d-1][N-2]%mod*fastpow(N-1,N-2-d+1)+A[N])%mod;
        A[N]=1ll*N*A[N]%mod;
        if (N>1) st[N]=fastpow(N,N-2);
    }
    f[0]=1;
    f[1]=1;
    for (int i=2; i<=5000; i++)
        for (int j=0; j<i; j++)
            f[i]=(1ll*C[j][i-1]*f[i-j-1]%mod*st[j+1]+f[i])%mod;
    for (int N=2; N<=5000; N++)
        for (int i=1; i<=N; i++)
            F[N]=(1ll*C[i-1][N-1]*((1ll*st[i]*F[N-i]%mod+1ll*f[N-i]*A[i]%mod)%mod)+F[N])%mod;
    while(t--)
    {
        scanf("%d",&n);
        printf("%d\n",F[n]);
    }
}
View Code

 

posted @ 2020-08-02 20:22  mdID(WWWZZZQQQ)  阅读(179)  评论(0)    收藏  举报