C14【模板】点分树 动态点分治

视频链接:277 点分树 动态点分治_哔哩哔哩_bilibili

 

Luogu P6329 【模板】点分树 | 震波

#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;
}

 

posted @ 2022-08-31 16:57  董晓  阅读(398)  评论(0编辑  收藏  举报