C A National Pandemic(树上点扩减值,树剖)
题:https://ac.nowcoder.com/acm/contest/5672/C
题意:给定树,m个操作:(1)在x点处增加w,树上每个点y的值+=w-dis(x,y)。
(2)将x点处值和0取min
(3)查询x点的值
分析:将每个点x 的值展开=w-dep(x)-dep(y)+2*dep(lca)。我分成(w-dep(x)),-(dep(y)),(2*dep(lca))来算
对于前俩部分我们很容易在边操作时边统计出来,对于第三部分我们用树剖维护,若在x进行1操作,那么就在x与根节点路径上的所有点都加2,查询就查询出的x'到根节点路径和就是第三部分的值(因为若要查询当前的x',x'与之前x的lca的dep*2就为lca到根节点路径长*2)
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r const int inf=0x3f3f3f3f; const ll INF=1e18; const int M=2e5+5; const int mod=998244353; vector<int>g[M]; int n,m,tot,sz[M],fa[M],id[M],son[M],top[M],dep[M]; int tr[M<<2],lz[M<<2]; int lasans[M],lasans1[M],lasnum[M]; void dfs1(int u,int f){ sz[u]=1,fa[u]=f; dep[u]=dep[f]+1; for(auto v:g[u]){ if(v!=f){ dfs1(v,u); sz[u]+=sz[v]; if(!son[u]||sz[v]>sz[son[u]]) son[u]=v; } } } void dfs2(int u,int tp){ id[u]=++tot,top[u]=tp; if(son[u]) dfs2(son[u],tp); for(auto v:g[u]){ if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } } void build(int root,int l,int r){ tr[root]=lz[root]=0; if(l==r)return; int midd=(l+r)>>1; build(lson); build(rson); } void up(int root){ tr[root]=tr[root<<1]+tr[root<<1|1]; } void pushdown(int root,int l,int r){ if(lz[root]){ int midd=(l+r)>>1; tr[root<<1]+=(midd-l+1)*2*lz[root]; tr[root<<1|1]+=(r-midd)*2*lz[root]; lz[root<<1]+=lz[root]; lz[root<<1|1]+=lz[root]; lz[root]=0; return ; } } void update(int L,int R,int root,int l,int r){ if(L<=l&&r<=R){ tr[root]+=(r-l+1)*2; lz[root]++; return ; } pushdown(root,l,r); int midd=(l+r)>>1; if(L<=midd) update(L,R,lson); if(R>midd) update(L,R,rson); up(root); } int query(int L,int R,int root,int l,int r){ ///cout<<l<<"##"<<r<<endl; if(L<=l&&r<=R){ return tr[root]; } int midd=(l+r)>>1; pushdown(root,l,r); int res=0; if(L<=midd) res+=query(L,R,lson); if(R>midd) res+=query(L,R,rson); return res; } void solve1(int u){ while(u){ update(id[top[u]],id[u],1,1,n); u=fa[top[u]]; } } int solve2(int u){ int ans=0; while(u){ ans+=query(id[top[u]],id[u],1,1,n); u=fa[top[u]]; } return ans; } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); tot=0; for(int i=0;i<=n;i++) g[i].clear(),lasans[i]=lasans1[i]=lasnum[i]=0,son[i]=0; for(int u,v,i=1;i<n;i++){ scanf("%d%d",&u,&v); g[u].pb(v),g[v].pb(u); } dfs1(1,0); dfs2(1,1); build(1,1,n); int ans1=0; int num=0;///1操作插入的点数 while(m--){ int op,x,w; scanf("%d%d",&op,&x); if(op==1){ scanf("%d",&w); solve1(x); ans1+=w-dep[x]; num++; } else if(op==2){ int ans2=solve2(x); int ans=lasans[x]+(ans1-lasans1[x])-(num-lasnum[x])*dep[x]+ans2; if(ans<=0) lasans[x]=ans-ans2;///保持不变,所以保持前俩项 else lasans[x]=-ans2;///全不要,而ans2是单独算的,所以在下次算的时候会抵消这次-的 lasans1[x]=ans1; lasnum[x]=num; } else{ int ans2=solve2(x); int ans=lasans[x]+(ans1-lasans1[x])-(num-lasnum[x])*dep[x]+ans2; lasans[x]=ans-ans2; lasans1[x]=ans1; lasnum[x]=num; printf("%d\n",ans); } } } return 0; }