点分树/动态点分治

引入

点分治

点分治能够解决单次询问,但无法处理多次询问和修改,这是我们就需要用点分树来解决

简介

多次询问时我们并不需要每次去求重心,同样的,修改一个点也不需要整个树重新遍历一次

我们将每次分治的重心连边构成一棵虚树,那么这棵树上点到根节点的路径就是处理这个点的分治顺序

因此对于询问和修改我们都只需要遍历这条链即可,又由于分治的性质,链的长度一定是 \(O(\log n)\) 的

实现

以P6329 【模板】点分树 / 震波为例

我们先考虑单次询问:就是点分治的板题,我们定义 \(cnt_{u,i}\) 为以 \(u\) 为根的子树中距离小于等于 \(i\) 的权值和,\(rt(u)\) 为以 \(u\) 为根的子树的重心,那么对于每个重心 \(u\),它的贡献是 \(cnt_{u,k-dis(x,u)}-cnt_{rt(son(u)),k-dis(x,u)}\),换到点分树上就是 \(cnt_{u,k-dis(x,u)}-cnt_{son(u),k-dis(x,u)}\),我们直接在点分树上用动态开点线段树,一个点开两棵,用来维护 \(cnt_{u,dis(x,u)},cnt_{u,dis(fa(u),x)}\),对于每个询问和修改,对应更新链上的点即可,复杂度 \(O(n\log^2 n)\)

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define lson ls[p],l,mid
#define rson rs[p],mid+1,r
const int N=1e5+5;
vector<int> e[N];
int dep[N],siz[N],mx[N],ff[N],vis[N],n,q,a[N],rt,son[N],top[N],dfn[N],f[N],tim,lst;
struct segtree{
	int tr[N<<6],ls[N<<6],rs[N<<6],tot,rt[N];
	void pushup(int p){tr[p]=tr[ls[p]]+tr[rs[p]];}
	void update(int& p,int l,int r,int x,int v){
		if(!p) p=++tot;
		if(l==r){tr[p]+=v;return;}
		if(x<=mid) update(lson,x,v);
		else update(rson,x,v);
		pushup(p);
	}
	int query(int p,int l,int r,int L,int R){
		if(L>R) return 0;
		if(L<=l&&r<=R) return tr[p];
		int res=0;
		if(L<=mid) res+=query(lson,L,R);
		if(R>mid) res+=query(rson,L,R);
		return res;
	}
}t1,t2;
void dfs(int u,int fa){
	dep[u]=dep[fa]+1,f[u]=fa,siz[u]=1;
	for(auto v:e[u]){
		if(v==fa) continue;
		dfs(v,u);siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int t){
	top[u]=t,dfn[u]=++tim;
	if(son[u]) dfs2(son[u],t);
	for(auto v:e[u]){
		if(v==son[u]||v==f[u]) continue;
		dfs2(v,v);
	}
}
void get_rt(int u,int fa,int sum,int& rt){
	siz[u]=1,mx[u]=0;
	for(auto v:e[u]){
		if(v==fa||vis[v]) continue;
		get_rt(v,u,sum,rt);
		siz[u]+=siz[v],mx[u]=max(mx[u],siz[v]);
	}
	mx[u]=max(mx[u],sum-siz[u]);
	if(mx[u]<mx[rt]) rt=u;
}
void rebuild(int u,int sum){
	vis[u]=1;
	for(auto v:e[u]){
		if(vis[v]) continue;
		int sz=siz[v]<siz[u]?siz[v]:sum-siz[u];rt=0;
		get_rt(v,u,sz,rt);
		ff[rt]=u;rebuild(rt,sz);
	}
}
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=f[top[x]];
	}
	return dep[x]>dep[y]?y:x;
}
int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
void update(int x,int v){
	for(int i=x;i;i=ff[i]){
		t1.update(t1.rt[i],0,n-1,dis(i,x),v);
		if(ff[i]) t2.update(t2.rt[i],0,n-1,dis(ff[i],x),v);
	}
}
int query(int x,int k){
	int res=0,pre=0;
	for(int i=x;i;pre=i,i=ff[i]){
		res+=t1.query(t1.rt[i],0,n-1,0,k-dis(x,i));
		if(pre) res-=t2.query(t2.rt[pre],0,n-1,0,k-dis(x,i));
	}
	return res;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++){
		int x,y;cin>>x>>y;
		e[x].push_back(y),e[y].push_back(x);
	}
	mx[0]=1e9,rt=0;
	dfs(1,0);dfs2(1,1);
	get_rt(1,0,n,rt);rebuild(rt,n);
	for(int i=1;i<=n;i++) update(i,a[i]);
	while(q--){
		int op,x,k;cin>>op>>x>>k;
		x^=lst,k^=lst;
		if(op) update(x,k-a[x]),a[x]=k;
		else cout<<(lst=query(x,k))<<"\n";
	}
	return 0;
}
posted @ 2026-10-30 14:49  Rainesxiao  阅读(5)  评论(0)    收藏  举报