[BZOJ 4033] [HAOI2015] T1 【树形DP】

题目链接:BZOJ - 4033

 

题目分析

使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值。

这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Father[i] 之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值)。

然后DFS来求,枚举 i 的每个儿子 j,现在的 f[i][] 是包含了 [1, j-1] 子树,然后两重循环枚举范围是 [1, j - 1] 的子树总 Size 和 j 的 Size,来更新 f[i][],这样更新之后的 f[i][] 就是 [1, j] 子树的答案了。

这样的更新看起来是 O(n^3) 的,但是其实可以看做枚举了任意点对的LCA,所以复杂度其实是 O(n^2) 的。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long LL;

inline int gmin(int a, int b) {return a < b ? a : b;}

inline LL gmax(LL a, LL b) {return a > b ? a : b;}

const int MaxN = 2000 + 5;

int n, k;
int Father[MaxN], Size[MaxN];

struct Edge
{
	int v, w;
	Edge *Next;
} E[MaxN * 2], *P = E, *Point[MaxN];

inline void AddEdge(int x, int y, int z)
{
	++P; P -> v = y; P -> w = z;
	P -> Next = Point[x]; Point[x] = P;
}

LL Ans;
LL Temp[MaxN], f[MaxN][MaxN];
 
void Solve(int x, int Fa, int Num)
{
	Size[x] = 1;
	for (Edge *j = Point[x]; j; j = j -> Next)
	{
		if (j -> v == Fa) continue;
		Solve(j -> v, x, j -> w);
		for (int i = 0; i <= gmin(Size[x], k); ++i) Temp[i] = f[x][i];
		for (int p = 0; p <= gmin(Size[x], k); ++p)
			for (int q = 0; q <= gmin(Size[j -> v], k); ++q)
				f[x][p + q] = gmax(f[x][p + q], Temp[p] + f[j -> v][q]);
		Size[x] += Size[j -> v];
 	}
 	for (int i = 0; i <= gmin(Size[x], k); ++i)
 		f[x][i] += (LL)Num * (LL)(i * (k - i) + (Size[x] - i) * (n - Size[x] - k + i));
}

int main()
{
	scanf("%d%d", &n, &k);
	int x, y, z;
	for (int i = 1; i <= n - 1; ++i)
	{
		scanf("%d%d%d", &x, &y, &z);
		AddEdge(x, y, z); AddEdge(y, x, z);
	}
	Solve(1, 0, 0);
	cout << f[1][k] << endl;
	return 0;
}

  

posted @ 2015-07-06 20:19  JoeFan  阅读(948)  评论(0编辑  收藏  举报