2020牛客暑期多校训练营(第七场) Valuable Forests

原题
题目描述
在这里插入图片描述
样例
输入

5 1000000007
2
3
4
5
107

输出

2
24
264
3240
736935633

思路
以下是乍一眼看不懂的官方题解:
在这里插入图片描述
这种规律过于玄学,所以我们找到了另一种解题思路:
因为要用到 p r u f e r prufer 序列,所以大家可以参考一下这篇博客

  • 因为n个点的无根树可以形成nn−2个不同的树,所以我们不妨设它的值为 d p n dp_n ,设n个点的森林个数为 f n f_n 。在第n个点加入时,我们可以选择 i i 个点和它形成一棵树,那么就可以列出 d p dp 式:
    d p n = i = 0 n 1 C n 1 i f ( n i 1 ) b i + 1 dp_n=\sum_{i=0}^{n-1}C_{n-1}^if(n-i-1)b_{i+1}
  • 然后我们可以再定义一个n个点能形成的所有无根树的权值和为 a n a_n ,并枚举它的度数为 i i ,列出 d p dp 式:
    a n = n i = 1 n 1 a_n=n\sum_{i=1}^{n-1} i i 2 C n 2 i 1 C_{n-2}^{i-1} ( n 1 ) (n-1) (n-1-d)
  • 最后我们定义 a n s n ans_n 为答案,列出 d p dp 式:
    a n s n = i = 0 n 1 ans_n=\sum_{i=0}^{n-1} C n 1 i C_{n-1}^{i} * ( ( b i + 1 a n s n i 1 + d p n i 1 a i + 1 ) b_{i+1}*ans_{n-i-1}+dp_{n-i-1}*a_{i+1})
    我们只要根据以上公式打表即可,具体细节可以看看代码。
    代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5010;
ll t,n,mod,c[maxn][maxn],f[maxn],p[maxn],dp[maxn],ans[maxn];
int ksm(ll a,ll b)//这里要开long long,快速幂加快运算
{
    if(b<=0)return 1;
    ll k=1;
    while(b)
    {
        if(b&1)k=k*a%mod;
        b>>=1;a=(a*a)%mod;
    }
    return k;
}
int main()
{
    scanf("%lld%lld",&t,&mod);c[0][0]=f[0]=f[1]=p[0]=p[1]=1;
    for(int i=1;i<=5000;i++)
    {
        c[0][i]=1;
        for(int j=1;j<=i;j++)c[j][i]=(c[j][i-1]+c[j-1][i-1])%mod;
    }
    for(int i=1;i<=5000;i++)
    {
        for(int j=1;j<i;j++)dp[i]=(j*j%mod*c[j-1][i-2]%mod*ksm(i-1,i-1-j)%mod+dp[i]%mod)%mod;
        dp[i]=i*dp[i]%mod;
        if(i^1)p[i]=ksm(i,i-2);
    }
    for(int i=2;i<=5000;i++)
        for(int j=0;j<i;j++)
            f[i]=(c[j][i-1]*f[i-j-1]%mod*p[j+1]%mod+f[i])%mod;
    for(int i=2;i<=5000;i++)
        for(int j=1;j<=i;j++)
            ans[i]=(c[j-1][i-1]*((p[j]*ans[i-j]%mod+f[i-j]*dp[j]%mod)%mod)+ans[i]%mod)%mod;
    while(t--)scanf("%lld",&n),printf("%lld\n",ans[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/bbbll123/article/details/107751044
今日推荐