Valuable Forests
题目描述:
我们将无根树T的权值定义为 ∑ u ∈ V ( T ) ( d ( u ) ) 2 \sum_{u\in V(T)}(d(u))^2 ∑u∈V(T)(d(u))2,其中 V ( T ) V(T) V(T)是 T T T的所有顶点的集合,而 d ( u ) d(u) d(u)是顶点 u u u的度。 我们将森林的价值定义为森林中所有树木的价值之和。 现在,我们希望您用 N N N个标记的顶点来回答所有森林的值之和。 为了避免计算巨大的整数,请以模 M M M为单位报告答案。
输入描述:
有多个测试用例。 输入的第一行包含两个整数 T T T和 M ( 1 ≤ T ≤ 5000 , 1 ≤ M ≤ 2 30 , M M(1 \le T \le 5000,1 \le M \le 2 ^ {30},M M(1≤T≤5000,1≤M≤230,M是质数 ) ) ),指示测试用例的数量和模数。对于每个测试用例,唯一的行仅包含整数 N ( 1 ≤ N ≤ 5000 ) N(1 \le N \le 5000) N(1≤N≤5000)。
输出描述:
对于每个测试用例,输出答案模 M M M的值。
样例输入:
5 1000000007
2
3
4
5
107
样例输出:
2
24
264
3240
736935633
思路:
题意:我们定义一棵无根树的权值是所有点的度数的平方和,求有标号的n个点的所有森林的权值的和。
首先,我们要知道
p
r
u
f
e
r
prufer
prufer序列这个东西:
p
r
u
f
e
r
prufer
prufer序列详解 ,由
p
r
u
f
e
r
prufer
prufer序列的结论可以知道:对于一棵有
n
n
n个节点的无根树,可以形成
n
n
−
2
n^{n-2}
nn−2棵不同的树。
现在计这个值为
s
u
m
sum
sum,我们就可以求出对于
n
n
n个节点的森林个数
f
n
f_n
fn:
f
(
n
)
=
∑
i
=
0
n
−
1
C
n
−
1
i
f
(
n
−
i
−
1
)
∗
s
u
m
(
i
+
1
)
f(n)=\mathop{\sum}\limits_{i=0}^{n-1}C^i_{n-1}f(n-i-1)*sum(i+1)
f(n)=i=0∑n−1Cn−1if(n−i−1)∗sum(i+1)
随后我们就可以推出
n
n
n个点可以形成所有的无根树的权值和
d
p
n
dp_n
dpn:
d
p
(
n
)
=
∑
i
=
1
n
∑
j
=
1
n
−
1
j
2
C
n
−
2
j
−
1
∗
(
n
−
1
)
n
−
j
−
1
dp(n)=\mathop{\sum}\limits_{i=1}^{n}\mathop{\sum}\limits_{j=1}^{n-1}j^2C^{j-1}_{n-2}*(n-1)^{n-j-1}
dp(n)=i=1∑nj=1∑n−1j2Cn−2j−1∗(n−1)n−j−1
根据
p
r
u
f
e
r
prufer
prufer序列的性质,如果节点
i
i
i的度数为
j
j
j,那么他的贡献可以看成
j
2
j^2
j2与序列中有且仅有
j
−
1
j-1
j−1个
i
i
i的方案数之积。
这时我们就发现这个
i
i
i没什么用…于是可以化简:
d
p
(
n
)
=
n
∑
j
=
1
n
−
1
j
2
C
n
−
2
j
−
1
∗
(
n
−
1
)
n
−
j
−
1
dp(n)=n\mathop{\sum}\limits_{j=1}^{n-1}j^2C^{j-1}_{n-2}*(n-1)^{n-j-1}
dp(n)=nj=1∑n−1j2Cn−2j−1∗(n−1)n−j−1
最后,我们就可以求出
n
n
n个点可以形成的森林的权值和
A
n
s
n
Ans_n
Ansn:
A
n
s
(
n
)
=
∑
i
=
0
n
−
1
C
n
−
1
i
∗
(
s
u
m
(
i
+
1
)
∗
A
n
s
(
n
−
i
−
1
)
+
f
(
n
−
i
−
1
)
∗
d
p
(
i
+
1
)
)
Ans(n)=\mathop{\sum}\limits_{i=0}^{n-1}C^i_{n-1}*(sum(i+1)*Ans(n-i-1)+f(n-i-1)*dp(i+1))
Ans(n)=i=0∑n−1Cn−1i∗(sum(i+1)∗Ans(n−i−1)+f(n−i−1)∗dp(i+1))
这样我们就预处理出了所有的
A
n
s
Ans
Ans,再
O
(
1
)
O(1)
O(1)查询一下就可以啦
A C AC AC C o d e Code Code:
#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);
}
}