「雅礼集训2018」树
Posted zsbzsb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「雅礼集训2018」树相关的知识,希望对你有一定的参考价值。
我们不难发现,(1) 号点和 (2) 号点肯定是固定了形态的。
那么我们就考虑从这两个节点的状态开始转移。
设 (dp_{i, j}) 表示树的总大小为 (i),深度为 (j) 的方案数。
那么答案就是 (frac{1}{(n - 1)!}sum_{i = 1} ^ n dp_{n, i} imes i),根据期望的线性性可得。
那么我们考虑如何转移:
首先我们显然有 (dp_{1, 1} = dp_{2, 2} = 1)。
对于 (dp_{i, j}) ,我们有两种转移:
[dp_{i, j} =
egin{cases}
sum_{k = 1}^{i - 2} sum_{l = 1}^k {i - 2 choose k - 1} imes dp_{k, l} imes dp_{i - k, j}\sum_{k = 1}^{i - 1} sum_{l = 1}^j {i - 2 choose k - 1} imes dp_{k, j} imes dp_{i - k, l}\end{cases}
]
第一种转移中,我们考虑枚举 (2) 节点的子树(不包括本身)的大小,然后钦定最大深度不在这部分子树里面,第二种转移反之。
记得要用 double
和 int
算两次答案。
参考代码:
#include <algorithm>
#include <cstdio>
using namespace std;
const int _ = 30;
int n, p, C[_][_];
namespace task1 {
double dp[_][_];
double fac(int x) {
double res = 1.0;
for (int i = 1; i <= x; ++i) res = res * i;
return res;
}
void main() {
dp[1][1] = dp[2][2] = 1;
for (int i = 3; i <= n; ++i)
for (int j = 2; j <= i; ++j) {
for (int k = 1; k <= i - 2; ++k)
for (int l = 1; l <= min(j - 2, k); ++l)
dp[i][j] += dp[k][l] * dp[i - k][j] * C[i - 2][k - 1];
for (int k = 1; k <= i - 1; ++k)
for (int l = 1; l <= j; ++l)
dp[i][j] += dp[k][j - 1] * dp[i - k][l] * C[i - 2][k - 1];
}
double ans = 0;
for (int i = 1; i <= n; ++i) ans += i * dp[n][i];
printf("%d
", (int) (ans / fac(n - 1) + 0.5));
}
}
namespace task2 {
int dp[_][_];
int fac(int x) {
int res = 1;
for (int i = 1; i <= x; ++i) res = 1ll * res * i % p;
return res;
}
int power(int x, int k) {
int res = 1;
for (; k; k >>= 1, x = 1ll * x * x % p)
if (k & 1) res = 1ll * res * x % p;
return res % p;
}
void main() {
dp[1][1] = dp[2][2] = 1;
for (int i = 3; i <= n; ++i)
for (int j = 2; j <= i; ++j) {
for (int k = 1; k <= i - 2; ++k)
for (int l = 1; l <= min(j - 2, k); ++l)
dp[i][j] = (dp[i][j] + 1ll * dp[k][l] * dp[i - k][j] % p * C[i - 2][k - 1] % p) % p;
for (int k = 1; k <= i - 1; ++k)
for (int l = 1; l <= j; ++l)
dp[i][j] = (dp[i][j] + 1ll * dp[k][j - 1] * dp[i - k][l] % p * C[i - 2][k - 1] % p) % p;
}
int ans = 0;
for (int i = 1; i <= n; ++i)
ans = (ans + 1ll * i * dp[n][i] % p) % p;
printf("%lld
", 1ll * ans * power(fac(n - 1), p - 2) % p);
}
}
int main() {
scanf("%d %d", &n, &p);
for (int i = 0; i <= n; ++i) C[i][0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j)
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
task1 ::main();
task2 ::main();
return 0;
}
以上是关于「雅礼集训2018」树的主要内容,如果未能解决你的问题,请参考以下文章
loj6029「雅礼集训 2017 Day1」市场 线段树+均摊分析