题意:
给出一棵 (n)个节点的树,每个节点有三个值:(a[i],b[i],c[i]),分别为该点的花费,该点的当前状态,该点的目标状态。
每次可以选择点 (u) 的子树中的 (k) 个点,将它们的当前状态进行重新排序,使之达到目标状态,花费为 (k*a[u])。求出最小的花费,使得所有的点达到目标状态。
数距范围:(1≤n≤2⋅10^5,1≤a_i≤10^9,0≤b_i,c_i≤1)
分析:
把最小值按路径从上到下传递下去,如果当前点的花费不大于祖先中的最小花费,那么可以以该点为根进行处理。
代码:
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int inf=0x3f3f3f3f;
vector<int>pic[N];
int a[N],b[N],c[N],num[N][2];
int n;
ll ans;
void dfs(int v,int p,int minn)
{
if(b[v]!=c[v])
{
if(b[v]==0) num[v][0]++;
else num[v][1]++;
}
for(int i=0;i<pic[v].size();i++)
{
int u=pic[v][i];
if(u==p) continue;
dfs(u,v,min(minn,a[v]));
num[v][0]+=num[u][0];
num[v][1]+=num[u][1];
}
if(minn>=a[v])//保证贪心
{
int t=min(num[v][0],num[v][1]);
ans+=2LL*t*a[v];
num[v][0]-=t;
num[v][1]-=t;
}
}
int main()
{
int u,v;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
pic[u].pb(v);
pic[v].pb(u);
}
ans=0;
dfs(1,0,inf);
if(num[1][0]||num[1][1])
printf("-1
");
else
printf("%lld
",ans);
return 0;
}