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

題目連接:BZOJ - 4033
php

 

題目分析

使用樹形DP,用 f[i][j] 表示在以 i 爲根的子樹,有 j 個黑點的最大權值。ios

這個權值指的是,這個子樹內部的點對間距離的貢獻,以及 i 和 Father[i] 之間的邊對答案的貢獻(好比這條邊對黑點對距離和的貢獻就是子樹內部的黑點數 * 子樹外部的黑點數 * 這條邊的權值)。spa

而後DFS來求,枚舉 i 的每一個兒子 j,如今的 f[i][] 是包含了 [1, j-1] 子樹,而後兩重循環枚舉範圍是 [1, j - 1] 的子樹總 Size 和 j 的 Size,來更新 f[i][],這樣更新以後的 f[i][] 就是 [1, j] 子樹的答案了。blog

這樣的更新看起來是 O(n^3) 的,可是其實能夠看作枚舉了任意點對的LCA,因此複雜度實際上是 O(n^2) 的。get

 

代碼

#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;
}
相關文章
相關標籤/搜索