LOJ#6495. 「雅礼集训 2018 Day1」树 题解
这个题训练的时候做了,结果只有两个人过,而且还有一个写的是状压DP……
令 \(dp_{n,d}\) 表示n个点,最大深度为d的树的个数.
转移分为两种情况:
1、1号节点只有1个子树。此时的DP值为 \(dp_{n-1,d-1}.\)
2、1号节点有多于一个子树。由于2号节点的父亲一定是1,我们考虑枚举2号节点所在子树的大小和最大深度然后暴力转移,复杂度 \(\Theta(N^4).\)
精细实现应该可以做到 \(\Theta(N^3).\)
至于求答案在 \(\mod p\) 意义下取整的问题,可以考虑使用long double
或__int128.
代码 :
#include <bits/stdc++.h>
#define LL __int128
using namespace std;
inline void print(LL x){
if (x > 9) print(x/10); putchar(x%10+'0');
}
inline int power(int x,int y,int P){
static int r; r = 1;
while (y){ if (y&1) r = 1ll * r * x % P; x = 1ll * x * x % P; y >>= 1; }
return r;
}
LL C[25][25],dp[25][25];
int main(){
int i,j,k,s,t;
for (i = 0; i <= 24; ++i) for (j = 0; j <= i; ++j) C[i][j] = (i==j || !i || !j) ? 1 : (C[i-1][j-1] + C[i-1][j]);
dp[1][1] = 1;
for (i = 2; i <= 24; ++i)
for (j = 1; j <= 24; ++j){
dp[i][j] = dp[i-1][j-1];
for (s = 1; s <= i-2; ++s){
for (t = 1; t <= s && t < j-1; ++t) dp[i][j] += C[i-2][s-1] * dp[s][t] * dp[i-s][j];
LL tot = 0;
for (k = 1; k <= j; ++k) tot += dp[i-s][k];
dp[i][j] += C[i-2][s-1] * dp[s][j-1] * tot;
}
}
int P,n; cin >> n >> P;
LL sumd = 0,frac = 1;
for (i = 1; i <= n; ++i) sumd += i * dp[n][i];
for (i = 1; i < n; ++i) frac *= i;
print((sumd * 2 + frac) / frac / 2),putchar('\n');
sumd = sumd % P * power(frac % P,P-2,P) % P;
print(sumd),putchar('\n');
return 0;
}