Valuable Forests
题目描述:
我们将无根树T的权值定义为 ,其中 是 的所有顶点的集合,而 是顶点 的度。 我们将森林的价值定义为森林中所有树木的价值之和。 现在,我们希望您用 个标记的顶点来回答所有森林的值之和。 为了避免计算巨大的整数,请以模 为单位报告答案。
输入描述:
有多个测试用例。 输入的第一行包含两个整数 和 是质数 ,指示测试用例的数量和模数。对于每个测试用例,唯一的行仅包含整数 。
输出描述:
对于每个测试用例,输出答案模 的值。
样例输入:
5 1000000007
2
3
4
5
107
样例输出:
2
24
264
3240
736935633
思路:
题意:我们定义一棵无根树的权值是所有点的度数的平方和,求有标号的n个点的所有森林的权值的和。
首先,我们要知道
序列这个东西:
序列详解 ,由
序列的结论可以知道:对于一棵有
个节点的无根树,可以形成
棵不同的树。
现在计这个值为
,我们就可以求出对于
个节点的森林个数
:
随后我们就可以推出
个点可以形成所有的无根树的权值和
:
根据
序列的性质,如果节点
的度数为
,那么他的贡献可以看成
与序列中有且仅有
个
的方案数之积。
这时我们就发现这个
没什么用…于是可以化简:
最后,我们就可以求出
个点可以形成的森林的权值和
:
这样我们就预处理出了所有的
,再
查询一下就可以啦
:
#include <bits/stdc++.h>
using namespace std;
const int N = 5000;
int t, n, mod, C[N + 5][N + 5], dp[N + 5], sum[N + 5], f[N + 5], ans[N + 5];
int ksm(int x, int y, int ret)
{
while (y)
{
if(y & 1) ret = ret * 1ll * x % mod;
x = ( 1ll * x * x ) % mod;
y >>= 1;
}
return ret % mod;
}//快速幂
int main()
{
scanf("%d%d", &t, &mod);
C[0][0] = sum[0] = sum[1] = f[0] = f[1] = 1;
for (int i = 1; i <= N; ++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;
}//杨辉三角组合数打表
for (int i = 1; i <= N; ++i)
{
for (int j = 1; j < i; ++j)
dp[i] = ( ( ( 1ll * j * j *C[j-1][i-2] ) % mod * ksm( i-1 ,i-j-1 ,1 ) ) % mod + dp[i] ) % mod;
dp[i] = ( 1ll * i * dp[i] ) % mod;
if(i ^ 1) sum[i] = ksm( i, i-2, 1);
}
for (int i = 2; i <= N; ++i)
for (int j = 0; j < i; ++j)
f[i] = ( ( 1ll * C[j][i-1] * f[i-j-1] ) % mod * sum[j+1] + f[i] ) % mod;
for (int i = 2; i <= N; ++i)
for (int j = 1; j <= i; ++j)
ans[i] = ( 1ll * C[j-1][i-1] * ( ( 1ll * sum[j] * ans[i-j] ) % mod + 1ll * f[i-j] * dp[j] % mod ) % mod + ans[i] ) % mod;
while (t--)
{
scanf("%d", &n);
printf("%d\n", ans[n] % mod);
}
}