C14【模板】点分树 动态点分治
视频链接:277 点分树 动态点分治_哔哩哔哩_bilibili
#include <algorithm> #include <cstdio> #include <cstring> using namespace std; const int N=100010; int n,m,w[N]; int idx,h[N],to[N*2],ne[N*2]; void add(int x,int y){ to[++idx]=y;ne[idx]=h[x];h[x]=idx; } struct LCA{ //最近公共祖先 int dep[N],fa[N][20]; void dfs(int u, int f){ dep[u]=dep[f]+1; fa[u][0]=f; for(int i=1; i<=19; i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=h[u]; i; i=ne[i]) if(to[i]!=f) dfs(to[i], u); } int lca(int u, int v){ if(dep[u]<dep[v])swap(u, v); for(int i=19; ~i; i--) if(dep[fa[u][i]]>=dep[v])u=fa[u][i]; if(u==v) return v; for(int i=19; ~i; i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i], v=fa[v][i]; return fa[u][0]; } int getdis(int x,int y){ //计算两点在原树中的距离 return dep[x]+dep[y]-2*dep[lca(x,y)]; } } lca; struct SegTree{ //线段树 int cnt,root[N],sum[N*40],lc[N*40],rc[N*40]; void change(int &u,int l,int r,int d,int v){ if(!u) u=++cnt; if(l==r){sum[u]+=v; return;} int mid=l+r>>1; if(d<=mid) change(lc[u],l,mid,d,v); else change(rc[u],mid+1,r,d,v); sum[u]=sum[lc[u]]+sum[rc[u]]; } int query(int u,int l,int r,int x,int y){ if(!u || r<x || l>y) return 0; if(x<=l&&r<=y) return sum[u]; int mid=l+r>>1; return query(lc[u],l,mid,x,y) +query(rc[u],mid+1,r,x,y); } } sg,ch; struct PointTree{ //点分树 int root,sum,siz[N],mxp[N],del[N]; void findwc(int x,int fa){ //找重心 siz[x]=1; mxp[x]=0; for(int i=h[x]; i; i=ne[i]) if(to[i] != fa && !del[to[i]]){ findwc(to[i],x); siz[x] += siz[to[i]]; mxp[x]=max(mxp[x],siz[to[i]]); } mxp[x]=max(mxp[x],sum-siz[x]); if(mxp[x]<mxp[root]) root=x; } void getroot(int x,int size){ //找出根 mxp[root=0]=sum=size; findwc(x,-1); findwc(root,-1);//确保siz[]正确 } int dis[N][20],dep[N],fa[N]; void build_sg(int x,int fa,int wc,int d){ sg.change(sg.root[wc],0,n,d,w[x]); for(int i=h[x]; i; i=ne[i]) if(to[i] != fa && !del[to[i]]) build_sg(to[i],x,wc,d+1); } void build_ch(int x,int fa,int wc,int d){ ch.change(ch.root[wc],0,n,d,w[x]); for(int i=h[x]; i; i=ne[i]) if(to[i] != fa && !del[to[i]]) build_ch(to[i],x,wc,d+1); } void build(int x){ //建点分树 del[x]=true; build_sg(x,-1,x,0); for(int i=h[x]; i; i=ne[i]){ if(del[to[i]])continue; getroot(to[i],siz[to[i]]); build_ch(to[i],-1,root,1); fa[root]=x; dep[root]=dep[x]+1; build(root); } } void init(){ getroot(1,n); //找根 build(root); //建树 lca.dfs(1,-1); //求lca for(int i=1; i<=n; i++) //原树中的点距 for(int j=i; j; j=fa[j]) dis[i][dep[i]-dep[j]]=lca.getdis(i,j); } int query(int x,int y){ int res=sg.query(sg.root[x],0,n,0,y); for(int i=x; fa[i]; i=fa[i]){ int d=dis[x][dep[x]-dep[fa[i]]]; res+=sg.query(sg.root[fa[i]],0,n,0,y-d); res-=ch.query(ch.root[i],0,n,0,y-d); } return res; } void change(int x,int y){ sg.change(sg.root[x],0,n,0,y-w[x]); for(int i=x; fa[i]; i=fa[i]){ int d=dis[x][dep[x]-dep[fa[i]]]; sg.change(sg.root[fa[i]],0,n,d,y-w[x]); ch.change(ch.root[i],0,n,d,y-w[x]); } } } pt; int main(){ int u,v,op,x,y,last=0; scanf("%d%d",&n,&m); for(int i=1; i <= n; i++) scanf("%d",&w[i]); for(int i=1; i < n; i++) scanf("%d%d",&u,&v),add(u,v),add(v,u); pt.init(); while(m--){ scanf("%d%d%d",&op,&x,&y); x^=last; y^=last; if(op == 0){ last=pt.query(x,y); printf("%d\n",last); } else pt.change(x,y), w[x]=y; } return 0; }