主要思路
啊,又是无根树 DP。首先讨论奇偶性,如果是奇数的话可以直接 DP;如果不是,则需要容斥一波。
设置状态 \(dp[scale][maxPart][degree]\),意为大小为 \(scale\)、最大子树大小为 \(maxPart\) 且根度数为 \(degree\) 的方案树。转移其实很好想,看代码吧。
代码
// CF724F.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1010, MAX_D = 12;
int n, d, mod, inv[MAX_D], dp[MAX_N][MAX_N][MAX_D];
bool vis[MAX_N][MAX_N][MAX_D];
int fpow(int bas, int tim)
{
int ret = 1;
while (tim)
{
if (tim & 1)
ret = 1LL * ret * bas % mod;
bas = 1LL * bas * bas % mod;
tim >>= 1;
}
return ret;
}
int solve(int scale, int max_part, int cdeg)
{
if (vis[scale][max_part][cdeg])
return dp[scale][max_part][cdeg];
vis[scale][max_part][cdeg] = true;
int &ret = dp[scale][max_part][cdeg];
if (scale == 1)
return ret = (cdeg == 0);
if (cdeg == 0 || max_part == 0 || cdeg >= scale)
return ret = 0;
if (max_part == 1)
return ret = (scale == cdeg + 1);
ret = solve(scale, max_part - 1, cdeg);
int part = solve(max_part, max_part, d - 1);
if (part == 0)
return ret;
int binomial = 1;
for (int i = 1; i <= cdeg && 1LL * i * max_part <= scale - 1; i++)
{
binomial = 1LL * binomial * inv[i] % mod * (part + i - 1) % mod;
ret = (0LL + ret + 1LL * binomial * solve(scale - 1LL * i * max_part, max_part - 1, cdeg - i) % mod) % mod;
}
return ret;
}
int main()
{
scanf("%d%d%d", &n, &d, &mod);
for (int i = 1; i <= d; i++)
inv[i] = fpow(i, mod - 2);
if (n <= 2)
puts("1"), exit(0);
if (n & 1)
printf("%d\n", solve(n, n >> 1, d)), exit(0);
int part = solve(n >> 1, (n >> 1) - 1, d - 1);
part = 1LL * part * (part + 1) % mod * inv[2] % mod;
// printf("%d ", part);
part = (0LL + part + solve(n, (n >> 1) - 1, d)) % mod;
printf("%d\n", part);
return 0;
}
[…] 例题:https://kalorona.com/oi/cf-742f/ […]