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
", 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
", ans);
}
return 0;
}