以前口胡了好多发的动态点分治,写起来的时候才知道有多恶心.
其实很多动态点分治的题都很板子,但是都很难写,因为要将自己的信息传给儿子,对于每个点要维护自己的信息和子树在父亲中的信息.
动态点分治实际上就是把点分治的那棵分治树保存下来,在题目不改变树的形态的情况下,可以通过对于每一个点维护一个数据结构来满足将点分治可以做的事情动态化,每次带个log.(分治树最大高度log
像这题:【模板】点分树 | 震波
对于每一个点,我们就用线段树维护自己子树内与该点距离为k的点权,并用一棵线段树,维护自己子树内的点与父亲的距离为k的点权,这样我们就可以实现将贡献区分了,每次询问查询x点与祖先的距离,并用k-距离到线段树查询即可.
写起来也是非常的恶心,用了的求距离.
#include
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 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<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<tot[son[x]]) son[x]=s[i].y;
}
if(max(tot[son[x]],sz-tot[x])&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 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