luogu P6329 【模板】点分树 | 震波
点分树,又称动态点分治,是一个看上去非常神奇的东西。但是实际上非常朴素,就是把点分治时候的两层重心之间连上一条边。
这样的树有什么性质呢,发现和原树一点都对应不上,可以发现其高度为\(O(\log n)\)的,所以一些普通树上不能干的事情在点分树上可以做,比如暴力遍历一个点的祖先,或者将每个点子树塞到一个vector里,复杂度是\(O(\log n)\)的,并且\(u,v\)在点分树上的LCA在原树上一定在\(u,v\)之间的路径上。
先考虑这道题的不带修版本,我们可以暴力遍历祖先,然后求出祖先和当前这个点的距离,对于每个点子树内建立一个动态开点线段树,每个下标维护子树内的点到当前点原树上的距离的点权之和。这样直接查询即可。修改只要暴力跳祖先,然后改线段树里的东西即可。
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=100000+5,M=(N*200)+5,K=(1<<16)+5,mod=998244353,M1=998244353,M2=1e9+7,Mod=mod-1;const db eps=1e-5;
int R1[N],R2[N],n,m,k,x,y,La,op,fa[N],W[N],Si[N],f[N],vis[N],Ct,Rt;vector<int> S[N],G[N];
void GI(int x,int La){Ct++;for(int i:S[x]) i^La&&!vis[i]&&(GI(i,x),0);}
void DP(int x,int La){Si[x]=1;f[x]=0;for(int i:S[x]) i^La&&!vis[i]&&(DP(i,x),Si[x]+=Si[i],f[x]=max(f[x],Si[i]));f[x]=max(f[x],Ct-Si[x]);f[x]<f[Rt]&&(Rt=x);}
void BD(int x,int La){Ct=0;GI(x,La);Rt=0;DP(x,La);x=Rt;La&&(G[La].PB(x),fa[x]=La/*,cerr<<x<<' '<<La<<'\n'*/);vis[x]=1;for(int i:S[x]) !vis[i]&&(BD(i,x),0);}
namespace DIS{
int d[N],fa[N][20],lg[N];void Make(int x,int La){fa[x][0]=La;d[x]=d[La]+1;for(int i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];for(int i:S[x]) i^La&&(Make(i,x),0);}
void BD(){int i;for(i=2;i<=n;i++) lg[i]=lg[i/2]+1;Make(1,0);}
int LCA(int x,int y){d[x]<d[y]&&(swap(x,y),0);while(d[x]^d[y]) x=fa[x][lg[d[x]-d[y]]];if(x==y) return x;for(int i=lg[d[x]];~i;i--) fa[x][i]^fa[y][i]&&(x=fa[x][i],y=fa[y][i]);return fa[x][0];}
int calc(int x,int y){return d[x]+d[y]-2*d[LCA(x,y)];}
}
namespace Tree{
int cnt,L[M],R[M],f[M];void Ins(int x,int y,int &v,int l=0,int r=n){!v&&(v=++cnt);f[v]+=y;if(l==r) return;int m=l+r>>1;x<=m?Ins(x,y,L[v],l,m):Ins(x,y,R[v],m+1,r);}
int Qry(int x,int y,int v,int l=0,int r=n){if((x<=l&&r<=y)||!v) return f[v];int m=l+r>>1;return (x<=m?Qry(x,y,L[v],l,m):0)+(y>m?Qry(x,y,R[v],m+1,r):0);}
}
void Push(int x,int Lp,int &Ro){Tree::Ins(DIS::calc(x,Lp),W[x],Ro);for(int i:G[x]) Push(i,Lp,Ro);}
void Make(int x,int La){Push(x,x,R1[x]);La&&(Push(x,La,R2[x]),0);/*cerr<<CCF<<'\n';*/for(int i:G[x]) Make(i,x);}
int GA(int x,int y){int Lp=x,Id=x,Ans=0,Ds;while(x) (Ds=DIS::calc(x,Id))<=y&&(Ans+=Tree::Qry(0,y-Ds,R1[x]),Id^x&&(Ans-=Tree::Qry(0,y-Ds,R2[Lp]))),x=fa[Lp=x];return Ans;/*for(int i=1;i<=n;i++) DIS::calc(x,i)<=y&&(Ans+=W[i]);return Ans;*/}
void CG(int x,int y){int Id=x,Lp=x,Ds;while(x) Ds=DIS::calc(x,Id),Tree::Ins(Ds,y-W[Id],R1[x]),Lp^x&&(Tree::Ins(Ds,y-W[Id],R2[Lp]),0),x=fa[Lp=x];W[Id]=y;}
int main(){
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
int i,j;f[0]=1e9;scanf("%d%d",&n,&m);for(i=1;i<=n;i++) scanf("%d",&W[i]);for(i=2;i<=n;i++) scanf("%d%d",&x,&y),S[x].PB(y),S[y].PB(x);BD(1,0);DIS::BD();
for(i=1;i<=n;i++) !fa[i]&&(Rt=i);Make(Rt,0);while(m--)scanf("%d%d%d",&op,&x,&y),x^=La,y^=La,op?(CG(x,y),0):(printf("%d\n",La=GA(x,y)));
}