1. 程式人生 > 實用技巧 >樹上貪心 E. Tree Shuffling 暑訓第一天

樹上貪心 E. Tree Shuffling 暑訓第一天

樹上貪心 E. Tree Shuffling

題目大意:

給你一棵樹,這棵樹每一個點有三個值,分別是a,b,c,a表示這個點的權值,b表示這個點本來的值,c表示這個點想要變成的值(b和c都是0或者1),對於一棵子樹u,你可以選擇k個節點,然後這k個節點進行重新洗牌,花費是k*a[u],問是否可以通過重新洗牌使得所有的節點都變成想要的節點,是則輸出最小的花費,不是輸出-1。

題解:

這個題目其實不是特別難,但是有點繞,一開始又想錯了一部分,所以最後做的都想睡覺了。。。

這個維護一條鏈上的最小值,如果這個值是從根節點到現在位置的最小值,則更新所有可以更新的節點,如果不是則可知最小值在其祖先節點,從根節點開始維護最小值,從葉子節點開始更新。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
ll a[maxn],b[maxn],c[maxn],ans;
vector<int>G[maxn];
void add(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
void dfs1(int u,int pre,ll mins){
    mins=min(mins,a[u]);
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==pre) continue;
        dfs1(v,u,mins);
        b[u]+=b[v];
        c[u]+=c[v];
    }
    if(a[u]==mins){
//        printf("b=%lld c=%lld\n",b[u],c[u]);
        ll num = min(b[u],c[u]);
        ans+=num*2*a[u];
        b[u]-=num;
        c[u]-=num;
//        printf("u=%d pre=%d mins=%lld num=%lld ans=%lld b=%lld c=%lld\n",u,pre,mins,num,ans,b[u],c[u]);
    }
}


int main(){
    int n;
    int f1=0,f2=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        if(b[i]==c[i]) b[i]=c[i]=0;
        f1+=b[i],f2+=c[i];
    }
    if(f1!=f2){
        printf("-1\n");
        return 0;
    }
    for(int i=1;i<n;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    ans=0;
    dfs1(1,0,inf64);
    printf("%lld\n",ans);
    return 0;
}
/*
8
82 0 0
3 1 1
53 0 0
5 0 0
81 0 1
56 1 0
99 1 0
87 0 1
5 7
2 8
4 7
4 1
3 2
2 5
8 6
----
7
6 0 0
2 0 1
4 0 1
10 1 0
6 1 0
3 0 0
9 1 1
6 3
5 6
2 6
6 4
5 7
1 7

 */