题目链接
对于一个节点而言,我们考虑这个点会被额外操作几次,显然这个点的返祖边操作的次数都可以延申道当前节点来用,我们设当前节点的的儿子边边权的和为sum,返祖边的权值为w。
如果儿子的边边权的最大值在被祖先扣过之后还是超过了sum的一半,那么我们就不能两两连路径消,只能消最大的。否则,我们必然能两两连边消。
修改操作就是去掉之前的贡献,再加上新的贡献就行。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<
int n,m;
vector<pair<int,int>>v[N];
multiset<int>s[N];
int fa[N];
LL ans=0,sum[N];
int x[N],y[N],z[N],f[N];
LL per[N];
void dfs(int x,int y,int w){
fa[x]=w;
f[x]=y;
if(v[x].size()==1&&y)return;
sort(v[x].begin(),v[x].end(),[](pair<int,int>x,pair<int,int>y){
return x.se>y.se;
});
LL sa=0;
for(auto k:v[x]){
if(k.fi!=y){
s[x].insert(-k.se);
sum[x]+=k.se;
dfs(k.fi,x,k.se);
sa+=k.se;
}
}
if(v[x][0].se-w>=sa-v[x][0].se){
ans+=v[x][0].se-w;
per[x]=v[x][0].se-w;
}else{
ans+=max(0ll,(sa-w+1)/2);
per[x]=max(0ll,(sa-w+1)/2);
}
}
void del(int dx){
int l=x[dx],r=y[dx];
ans-=per[l]+per[r];
per[l]=per[r]=0;
}
void add(int dx,int dy){
int l=x[dx],r=y[dx],q=z[dx];
if(f[l]!=r)swap(l,r);
s[r].erase(s[r].find(-q));
sum[r]-=q;
z[dx]=dy;
s[r].insert(-dy);
sum[r]+=dy;
auto mx=-(*s[r].begin());
if(mx-fa[r]>=sum[r]-mx){
ans+=mx-fa[r];
per[r]=mx-fa[r];
}else{
ans+=max((sum[r]-fa[r]+1)/2,0ll);
per[r]=max((sum[r]-fa[r]+1)/2,0ll);
}
if(v[l].size()==1)return;
mx=-(*s[l].begin());
fa[l]=dy;
if(mx-fa[l]>=sum[l]-mx){
ans+=mx-fa[l];
per[l]=mx-fa[l];
}else{
ans+=max((sum[l]-fa[l]+1)/2,0ll);
per[l]=max((sum[l]-fa[l]+1)/2,0ll);
}
}
int main() {
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<n;i++){
int s,t,w;
cin>>s>>t>>w;
v[s].emplace_back(t,w);
v[t].emplace_back(s,w);
x[i]=s;
y[i]=t;
z[i]=w;
}
dfs(1,0,0);
cout<<ans<<'\n';
for(int i=1;i<=m;i++){
int dx,dy;
cin>>dx>>dy;
del(dx);
add(dx,dy);
cout<<ans<<'\n';
}
return 0;
}
/*
6 1
1 3 4
3 6 7
3 5 6
1 2 3
2 4 5
*/