题意:

分析:看到这道题目,我们直观的感受(贪心思考),对于一个节点i,如果i的祖先节点的花费比它小,显然可以让i作为这个祖先节点的子树中的某个节点,因为我们有一个高度更高并且花费更小的节点在它之上,并且管辖它。然后,我们还要考虑不行的局面,对于0-0、1-1的类型,我们没有必要去改动它,我们只需要改动0-1、1-0这种类型的节点,如果总的0-1、1-0不配对(奇偶性不一样),那么就是不可行的局面。我们自底向上的处理每个子树中0-1,1-0的对,看是否能配对,否则作为返回值返回给上面处理

这道题的想法思路不难,但是对设计搜索的能力要求非常高,等会我会继续编辑讲解怎么设计dfs。

根据我的经验,首先设计dfs的时候,要把循环分支的子问题当作已经处理好的,然后返回值是处理好多余的配对,如果上面存在一个花费更小,更高的节点,我们这个子树中的配对数量就不要配对了,往上传递再配对,还有就是本层节点的配对数也要加上,这样,基本上都能处理好dfs。


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

using namespace std;
const int N = 200005;
typedef pair<int, int> PII;

int cost[N], b[N], c[N];

int h[N], e[2 * N], ne[2 * N], idx;
long long res;
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

PII dfs(int u, int parent, int mn)
{
	//本层的遗留配对数,作为返回
	PII a = { 0, 0 };

	//初值和终值不一样
	if (b[u] != c[u])
	{
		if (b[u])
			++a.first;
		else
			++a.second;
	}

	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (j == parent) continue;
		PII p = dfs(j, u, min(mn, cost[u]));
		//加上从下面返回过来的遗留配对数
		a.first += p.first;
		a.second += p.second;
	}
	//计算,返回遗留配对数,并且判断是否能被u管辖
	if (cost[u] < mn)
	{
		int take = min(a.first, a.second);
		res += 2LL * take * cost[u];
		a.first -= take;
		a.second -= take;
	}
	return a;
}

int main()
{
	//n个节点
	int n;
	scanf("%d", &n);

	for (int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d", &cost[i], &b[i], &c[i]);
	}

	memset(h, -1, sizeof h);
	int u, v;
	for (int i = 1; i < n; ++i)
	{
		scanf("%d%d", &u, &v);
		add(u, v), add(v, u);
	}

	pair<int, int> ans = dfs(1, 0, 2e9);

	if (ans.first > 0 || ans.second > 0)
	{
		puts("-1");
	}
	else
	{
		printf("%lld\n", res);
	}

	return 0;
}