E. Tree Shuffling(贪心)

传送门

思路:树上贪心的经典题目,根据贪心思想,显然已经满足条件的结点不需要动,然后我们对每个结点记录它的子树中含有两种不满足条件 ( 1 , 0 ) , ( 0 , 1 ) (1,0),(0,1) (1,0),(0,1)的类型结点数,

然后还需要记录从根结点到当前结点 u u u之前的最小 c o s t cost cost. 然后对每个结点判断一下,

如果结点 u u u c o s t < m i n c o s t cost<min_{cost} cost<mincost就用这个结点对它的子树洗牌,最后再判断一下两种类型的结点数在 d f s dfs dfs后是否都为0就即可。

时间复杂度: O ( n ) O(n) O(n)

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,inf=0x3f3f3f3f;
#define mst(a) memset(a,0,sizeof a)
int a[N],b[N],c[N],n;
ll ans;
vector<int>e[N];
pair<int,int> dfs(int u,int fa,int mn){
	  pair<int,int>pi={0,0};
	  if(b[u]!=c[u]){
	  	if(b[u]) pi.first++;
	  	else pi.second++;
	  }
	  for(auto v:e[u]){
	  	 if(v==fa) continue;
	  	 pair<int,int>tmp=dfs(v,u,min(mn,a[u]));
	  	 pi.first+=tmp.first;
	  	 pi.second+=tmp.second;
	  }
	  if(a[u]<mn){
			int cnt=min(pi.first,pi.second);
	  	   ans+=1LL*cnt*a[u]*2;
	  	   pi.first-=cnt,pi.second-=cnt;
	  }
	  return pi;
} 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		e[u].push_back(v),e[v].push_back(u);
	}
	pair<int,int>res=dfs(1,0,inf);
	if(res.first||res.second) puts("-1");
	else printf("%lld\n",ans);
	return 0;
}