2020牛客暑期多校训练营(第十场)C.Decrement on the Tree

题目链接

对于一个节点而言,我们考虑这个点会被额外操作几次,显然这个点的返祖边操作的次数都可以延申道当前节点来用,我们设当前节点的的儿子边边权的和为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
 */

你可能感兴趣的:(思维)