Codeforces 735E 树形DP

题意:给你一棵树,你需要在这棵树上选择一些点染成黑色,要求染色之后树中任意节点到离它最近的黑色节点的距离不超过m,问满足这种条件的染色方案有多少种?

思路:设dp[x][i]为以x为根的子树中,离x点最近的点的距离是i,并且满足题目中的限制条件的方案数。我们假设已经计算了x的一些子树对x的贡献,我们考虑x的一个还没产生贡献的子树y对x的贡献。用数组f作为临时变量。我们枚举x的距离i和y的距离j,容易发现有两种情况:i + j + 1 <= 2 * m + 1,这种时候不会出现不符合题目条件的点,直接转移即可:f[min(i, j + 1)] += dp[x][i] * dp[y][j]。第二种情况:i + j + 1 > 2 *m + 1, 那么就不能直接转移了,因为如果转移了会出现不符合题目中的条件的点。但是我们可以保存这两者中的最大值,作为最远点来看他们仍然是合法的,而且它们仍有可能和后面的子树形成新的答案。有一个细节需要注意一下:在处理第一棵子树的时候,我们把dp[x][0]和dp[x][m + 1]设置为0,第一个的原因是因为子树和根节点本身可以形成合法情况。第二种操作相当于取了个巧,本质是把dp[v][i]赋值给dp[x][i + 1]。

代码:

#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define db double
#define pii pair<int, int>
using namespace std;
const LL mod = 1e9 + 7;
const int maxn = 210;
LL dp[maxn][maxn], f[maxn];
vector<int> G[maxn];
int n, m;
void add(int x, int y) {
	G[x].push_back(y);
	G[y].push_back(x);
}
void dfs(int x, int fa) {
	dp[x][0] = dp[x][m + 1] = 1;
	for (auto y : G[x]) {
		if(y == fa) continue;
		dfs(y, x);
		memset(f, 0, sizeof(f));
		for (int i = 0; i <= 2 * m + 1; i++) {
			for (int j = 0; j <= 2 * m; j++) {
				if(i + j <= 2 * m) {
					int p = min(i, j + 1);
					f[p] = (f[p] + (dp[x][i] * dp[y][j]) % mod) % mod;
				} 
				else {
					int p = max(i, j + 1);
					f[p] = (f[p] + (dp[x][i] * dp[y][j]) % mod) % mod;
				}
			}
		}
		for (int i = 0; i <= 2 * m; i++) {
			dp[x][i] = f[i];
		}
	}
}
int main() {
	int x, y;
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &x, &y);
		add(x, y);
	}
	dfs(1, -1);
	LL ans = 0;
	for (int i = 0; i <= m; i++) {
		ans = (ans + dp[1][i]) % mod;
	}
	printf("%lld\n", ans);
} 

  

posted @ 2019-08-30 19:15  维和战艇机  阅读(302)  评论(0编辑  收藏  举报