正题
以前口胡了好多发的动态点分治,写起来的时候才知道有多恶心.
其实很多动态点分治的题都很板子,但是都很难写,因为要将自己的信息传给儿子,对于每个点要维护自己的信息和子树在父亲中的信息.
动态点分治实际上就是把点分治的那棵分治树保存下来,在题目不改变树的形态的情况下,可以通过对于每一个点维护一个数据结构来满足将点分治可以做的事情动态化,每次带个log.(分治树最大高度log
像这题:【模板】点分树 | 震波
对于每一个点,我们就用线段树维护自己子树内与该点距离为k的点权,并用一棵线段树,维护自己子树内的点与父亲的距离为k的点权,这样我们就可以实现将贡献区分了,每次询问查询x点与祖先的距离,并用k-距离到线段树查询即可.
写起来也是非常的恶心,用了的求距离.
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
struct edge{
int y,nex;
}s[N<<1];
int first[N],len=0,T;
int rt[N][2],n,m,d[N],root,sz,mx=0,siz[N];
int sum[N*160],ls[N*160],rs[N*160],fa[N];
int son[N],tot[N],ans;
bool tf[N];
vector<int> P;
struct Lowest_Common_Ancestor{
int dep[N],dfn[N],lg[N<<1],tim;
int mmin[N<<1][20];
void dfs(int x,int fa){
dfn[x]=++tim;mmin[tim][0]=x;
for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa){
dep[s[i].y]=dep[x]+1;
dfs(s[i].y,x);
tim++;mmin[tim][0]=x;
}
}
void pre(){
lg[1]=0;for(int i=2;i<=2*n;i++) lg[i]=lg[i/2]+1;
dep[1]=1;dfs(1,0);
for(int i=2*(n-1);i>=1;i--){
for(int k=1;k<=19;k++) if(i+(1<<k)-1<=2*(n-1)){
if(dep[mmin[i][k-1]]<dep[mmin[i+(1<<(k-1))][k-1]]) mmin[i][k]=mmin[i][k-1];
else mmin[i][k]=mmin[i+(1<<(k-1))][k-1];
}
else break;
}
}
int Lca(int x,int y){
if(dfn[x]>dfn[y]) swap(x,y);
int tmp=lg[dfn[y]-dfn[x]+1];
int a=mmin[dfn[x]][tmp],b=mmin[dfn[y]+1-(1<<tmp)][tmp];
return dep[a]<dep[b]?a:b;
}
int gdis(int x,int y){return dep[x]+dep[y]-2*dep[Lca(x,y)];}
}LCA;
void ins(int x,int y){s[++len]=(edge){y,first[x]};first[x]=len;}
void fdrt(int x,int fa){
son[x]=0;tot[x]=1;
for(int i=first[x];i!=0;i=s[i].nex) if(!tf[s[i].y] && s[i].y!=fa){
fdrt(s[i].y,x);
tot[x]+=tot[s[i].y];
if(tot[s[i].y]>tot[son[x]]) son[x]=s[i].y;
}
if(max(tot[son[x]],sz-tot[x])<max(tot[son[root]],sz-tot[root])) root=x;
}
void gi(int&now,vector<int>&S,int l,int r){
if(!now) now=++T;
if(l==r){sum[now]=S[l];return ;}
int mid=(l+r)/2;
gi(ls[now],S,l,mid);
gi(rs[now],S,mid+1,r);
sum[now]=sum[ls[now]]+sum[rs[now]];
}
void gp(int x,int dep,int fa){
sz++;P.push_back(0);P[dep]+=d[x];
for(int i=first[x];i!=0;i=s[i].nex) if(!tf[s[i].y] && s[i].y!=fa)
gp(s[i].y,dep+1,x);
}
void build(int x,int tp){
root=0;fdrt(x,0);tf[x=root]=true;siz[x]=sz;fa[x]=tp;
if(siz[x]!=n) gi(rt[x][0],P,0,siz[x]);
vector<int> S;S.resize(siz[x]+1);S.clear();S[0]=d[x];
for(int i=first[x];i!=0;i=s[i].nex) if(!tf[s[i].y]){
P.resize(1);sz=0;gp(s[i].y,1,x);
for(int j=0;j<=sz;j++) S[j]+=P[j];
build(s[i].y,x);
}
gi(rt[x][1],S,0,siz[x]);
}
int gs(int now,int x,int y,int l,int r){
if(x==l && y==r) return sum[now];
int mid=(l+r)/2;
if(y<=mid) return gs(ls[now],x,y,l,mid);
else if(mid<x) return gs(rs[now],x,y,mid+1,r);
else return gs(ls[now],x,mid,l,mid)+gs(rs[now],mid+1,y,mid+1,r);
}
void add(int now,int x,int d,int l,int r){
sum[now]+=d;
if(l==r) return ;
int mid=(l+r)/2;
if(x<=mid) add(ls[now],x,d,l,mid);
else add(rs[now],x,d,mid+1,r);
}
void solve(int x,int y){
int now=x,dis=0;ans=0;
while(now){
if(dis<=y) ans+=gs(rt[now][1],0,min(siz[now],y-dis),0,siz[now]);
if(fa[now]){
dis=LCA.gdis(fa[now],x);
if(dis<=y) ans-=gs(rt[now][0],0,min(siz[now],y-dis),0,siz[now]);
}
now=fa[now];
}
printf("%d\n",ans);
}
void chg(int x,int y){
int now=x,dis=0;
while(now){
add(rt[now][1],dis,y-d[x],0,siz[now]);
if(fa[now]){
dis=LCA.gdis(fa[now],x);
add(rt[now][0],dis,y-d[x],0,siz[now]);
}
now=fa[now];
}
d[x]=y;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
int type,x,y;
for(int i=1;i<n;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
LCA.pre();sz=n;build(1,0);
while(m--){
scanf("%d %d %d",&type,&x,&y);
x^=ans;y^=ans;
if(type==0) solve(x,y);
else chg(x,y);
}
}

本文深入探讨了动态点分治的实现细节,介绍了如何通过维护点分治树和使用线段树来处理与点距离相关的查询,同时分享了一个具体的应用案例。
2765

被折叠的 条评论
为什么被折叠?



