1. 程式人生 > 實用技巧 >[2020牛客暑期多校訓練營(第十場)C Decrement on the Tree]

[2020牛客暑期多校訓練營(第十場)C Decrement on the Tree]

2020牛客暑期多校訓練營(第十場)C Decrement on the Tree

題目大意:

給你一顆 n 個節點的樹,每一條邊都有一個權值,有一種操作就是你可以選擇一條鏈,將鏈上的每一條邊的權值都 -x,問最少的操作使得這棵樹的權值變成0。

題解:

從葉子節點開始考慮:

對於這個圖,考慮最少的操作使得這棵樹權值變成0 。

  • 首先肯定是可以到父親節點就和父親節點來匹配,父親節點如果不能匹配則再兩兩匹配,如果兩兩不能匹配了,那麼就只能1條邊算一次貢獻了。
  • 如果父親節點的邊權值大於所有兒子節點,那麼兒子節點就直接和父親節點匹配
  • 如果沒有,那麼在兒子節點中找到最大值,如果兒子節點的最大值大於其他所有值之和,那麼就是兒子節點最大值減去其他所有值之和的貢獻再加上匹配的對數減去和父親節點匹配的對數,因為父親節點要和其父親節點再算。
  • 如果兒子節點中的最大值小於等於其他所有值之和,那麼直接求出對數減去父親節點匹配的對數。
  • 看不懂後面的三個分析可以直接看程式碼的 \(cal(u)\) 這個函式,這個函式的功能就是計算這個。

因為每一個點更新只會影響兩個點,所以這麼些複雜度肯定是過得去的。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define debug(x) cout<<"debug:"<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
multiset<ll> mulset[maxn];
int a[maxn],x[maxn],y[maxn],w[maxn];
int head[maxn],to[maxn<<1],nxt[maxn<<1],cnt;
void add(int u,int v){
    ++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
    ++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}
int dep[maxn],fa[maxn];
void dfs1(int u,int pre,int d){
    dep[u]=d,fa[u] = pre;
    for(int i=head[u];i;i=nxt[i]){
        int v = to[i];
        if(v == pre) continue;
        dfs1(v,u,d+1);
    }
}
ll sum[maxn],ans,dp[maxn];

ll cal(int u){
    ll now = 0;
    ll x = *mulset[u].rbegin();
    ll res = sum[u] - x;
    if(a[u]>=sum[u]-a[u]) ;
    else if(x<=res) now += sum[u]/2+sum[u]%2-a[u];
    else now+= x - a[u];
    return now;
}
void dfs2(int u){
    sum[u] = a[u];
    mulset[u].insert(a[u]);
    for(int i=head[u];i;i=nxt[i]){
        int v = to[i];
        if(v == fa[u]) continue;
        mulset[u].insert(a[v]);
        sum[u]+=a[v];
        dfs2(v);
    }
    dp[u] = cal(u);
    ans+=dp[u];
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&x[i],&y[i],&w[i]);
        add(x[i],y[i]);
    }
    dfs1(1,0,1);
    for(int i=1;i<n;i++){
        if(dep[x[i]]<dep[y[i]]) swap(x[i],y[i]);
        a[x[i]]=w[i];
    }
    ans = 0;
    dfs2(1);
    printf("%lld\n", ans);
    while(m--){
        int p,c;
        scanf("%d%d",&p,&c);
        int u = x[p],pre = fa[u];

        sum[u] = sum[u] - a[u] + c;
        mulset[u].erase(mulset[u].lower_bound(a[u]));
        mulset[u].insert(c);

        sum[pre] = sum[pre] - a[u] + c;
        mulset[pre].erase(mulset[pre].lower_bound(a[u]));
        mulset[pre].insert(c);

        a[u] = c;
        ans -= dp[u] + dp[pre];
        dp[u] = cal(u),dp[pre] = cal(pre);
        ans += dp[u] + dp[pre];
        printf("%lld\n", ans);
    }
    return 0;
}