【题解】BZOJ3257:树的难题

原题传送门
这真是一道难题qwq
看了%%%Claris的题解后懂了
树形DP

先设置状态, d p [ u ] [ i ] [ j ] dp[u][i][j] dp[u][i][j]表示以u为根的子树中黑点 i i i个,白点 j j j个,的答案
首先优化空间, i > = 1 i>=1 i>=1的情况等价于 i = 1 i=1 i=1,反正都是不满足要求的
j > = 2 j>=2 j>=2的情况等价于 j = 2 j=2 j=2,这样就把空间大大的减少

考虑转移
先把子树的信息综合到自己身上
再把自己的信息考虑进去
方程具体见代码

转移的时候需要分层,不能同一层中直接转,会wa
分层就直接多开一个数组记录一下

注意多组数据,别忘了初始化

Code:

#include 
#define maxn 300010
#define LL long long
using namespace std;
const LL inf = 1LL << 60;
struct Edge{
	int to, next, len;
}edge[maxn << 1];
LL dp[maxn][3][3], f[3][3], ans;
int num, head[maxn], color[maxn], n;

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

void addedge(int x, int y, int z){ edge[++num] = (Edge){ y, head[x], z }; head[x] = num; }
void upd(LL &x, LL y){ if (x > y) x = y; }

void dfs(int u, int pre){
	for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) dp[u][i][j] = inf;
	dp[u][0][0] = 0;
	for (int x = head[u]; x; x = edge[x].next){
		int v = edge[x].to;
		if (v != pre){
			dfs(v, u);
			for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) f[i][j] = inf;
			for (int i = 0; i <= 1; ++i)
			for (int j = 0; j <= 2; ++j)
				if (dp[u][i][j] < inf)
				for (int k = 0; k <= 1; ++k)
				for (int l = 0; l <= 2; ++l)
					if (dp[v][k][l] < inf){
						upd(f[i | k][min(j + l, 2)], dp[u][i][j] + dp[v][k][l]);
						if (!k || l < 2) upd(f[i][j], dp[u][i][j] + dp[v][k][l] + edge[x].len);
						//把一棵子树断掉需要满足!k||l<2
					}
			for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) dp[u][i][j] = f[i][j];
		}
	}
	for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) f[i][j] = inf;
	for (int i = 0; i <= 1; ++i)
	for (int j = 0; j <= 2; ++j)
		if (dp[u][i][j] < inf) upd(f[i | !color[u]][min(2, j + (color[u] == 1))], dp[u][i][j]);
		//把自己综合进去
	for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) dp[u][i][j] = f[i][j];
}

int main(){
	int M = read();
	while (M--){
		num = 0;
		memset(head, 0, sizeof(head));
		n = read();
		for (int i = 1; i <= n; ++i) color[i] = read();
		for (int i = 1; i < n; ++i){
			int x = read(), y = read(), z = read();
			addedge(x, y, z); addedge(y, x, z);
		}
		dfs(1, 0);
		ans = inf;
		for (int i = 0; i <= 1; ++i) 
			for (int j = 0; j <= 2; ++j)
				if (!i || j < 2) upd(ans, f[i][j]);
				//这边就直接用f数组了,其实dp[1][i][j]也一样
				//同样需要满足!i||j<2
		printf("%lld\n", ans);
	}
	return 0;
}

你可能感兴趣的:(题解,BZOJ,DP,树相关)