C Decrement on the Tree(选择路径所有边权减1,问多少次操作能让所有边权等于0)

题:https://ac.nowcoder.com/acm/contest/5675/C

题意:选择路径所有边权减1,问最少多少次操作能让所有边权等于0,支持边修改

分析:我们选择的路径只用考虑起点即可,对于一个节点我们用临边边权和来考量它;

   第一种情况为有一条边的边权大于总和的一半,假设差值为x,那么必然有从此节点出发的路径x条

   第二种情况为第一种情况没有发生,若总和为奇数,则必然有一条从此节点出发的路径,偶数则没有,因为一定会有由其他节点出发的路径经过它。  

   因此我们处理时只需要关注临边边权的最大值即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define pii pair<int,int>
#define MP make_pair
const int inf=0x3f3f3f3f;
const int M=2e5+5;
multiset<ll>g[M];
ll sum[M],w[M];
int x[M],y[M];
ll solve(int x){
    multiset<ll>::iterator it=g[x].end();
    it--;
    if(*it>sum[x]-*it)return 2*(*it)-sum[x];
    return sum[x]&1;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        scanf("%d%d%lld",&x[i],&y[i],&w[i]);
        g[x[i]].insert(w[i]);
        g[y[i]].insert(w[i]);
        sum[x[i]]+=w[i];
        sum[y[i]]+=w[i];
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
        ans+=solve(i);
    printf("%lld
",ans/2);
    while(m--){
        int op;
        ll val;
        scanf("%d%lld",&op,&val);
        ans-=solve(x[op])+solve(y[op]);
        g[x[op]].erase(g[x[op]].find(w[op]));
        g[y[op]].erase(g[y[op]].find(w[op]));
        sum[x[op]]+=val-w[op];
        sum[y[op]]+=val-w[op];
        w[op]=val;
        g[x[op]].insert(w[op]);
        g[y[op]].insert(w[op]);
        ans+=solve(x[op])+solve(y[op]);
        printf("%lld
",ans/2);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/13490236.html