【学习笔记】点分树

【学习笔记】点分树

点分树是一种动态维护树上信息的高级数据结构,就是在点分治的基础上支持在线修改操作,所以很多人也很喜欢叫它动态点分治。个人觉得非常暴力。

我们借助 【模板】点分树 | 震波 这道题来深入理解点分树的思想和实现过程。

友情提示:本题中较难理解的部分其实是 点分树上维护线段树,但纯粹的点分树只有建树部分!要想更容易地理解,建议先完成 P4178 Tree ,这是本题的弱化版,不带修,就是个点分治+线段树。

代码详解

点分树的过程:

  1. 建立点分树

    1. 找整棵树的重心u
    2. 从u进入,找各子树的重心v,并给u,v连边
    3. 第2步的同时给点分树上每个节点都建一棵动态开点线段树。
    4. 建立点分树上距离和实际距离之间的映射关系
  2. 修改和查询

    1. 与 x 相关的每一棵线段树都需要在对应处修改。

    2. 时刻记住sg和ch的定义:

      sg[u][d] : 在点分树上,以 u 为根的子树中(刚好包括了原树上以 u 为重心的子树的所有点),到 u 距离为 d 的所有点的权值之和。

      ch[u][d] : 在点分树上,以 u 为根的子树中(刚好包括了原树上以 u 为重心的子树的所有点),到 fa[u] 距离为 d 的所有点的权值之和。(fa[u]u 在点分树上的父亲)

一些犯过的错误:

  1. lca 不初始化,倍增不初始化(不适应大码量导致的)
  2. 点分树上修改查询,跳父节点的时候距离写成 y 而不是 yd
  3. 找重心时,不初始化最大连通块 mxp[u]=0
  4. 距离映射的时候for循环时 for(int i = x; i; i = fa[i]),修查的时候for循环是for(int i = x; fa[i]; i = fa[i]),并且其实修查的“研究对象”都是 fa[i] 不是 i,所以 d = dis[x][ dep[x] - dep[fa[i]] ] 而不是 d = dis[x][ dep[x] - dep[i] ]
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
using namespace std;
using ll = long long;
const int N = 2e5 + 5;
struct node{
	int v, ne;
}e[N << 1]; 
int first[N], idx = 0;
int n, m, w[N];
void add(int x, int y){
	e[++ idx] = (node){y, first[x]};
	first[x] = idx;
}
struct LCA{
	int dep[N], fa[N][20];
	void ini(int u, int f){
		dep[u] = dep[f] + 1;
		fa[u][0] = f;
		F(i, 1, 19) fa[u][i] = fa[fa[u][i - 1]][i - 1];
		for(int i = first[u]; i; i = e[i].ne){
			int v = e[i].v;
			if(v == f) continue;
			ini(v, u);
		}
	}
	int getlca(int x, int y){
		if(dep[x] < dep[y]) swap(x, y);
		G(i, 19, 0) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
		if(x == y) return x;
		G(i, 19, 0) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
		return fa[x][0];
	}
	int getdis(int x, int y){
		return dep[x] + dep[y] - 2 * dep[getlca(x, y)];
	}
}lca;

struct segtree{
	int cnt = 0, root[N], sum[N * 40], lc[N * 40], rc[N * 40];
	void update(int &u, int l, int r, int x, int y){
		if(!u) u = ++ cnt;
		if(l == r){
			sum[u] += y; 
			return ;
		}
		int mid = (l + r) >> 1;
		if(x <=  mid) update(lc[u], l, mid, x, y);
		else update(rc[u], mid + 1, r, x, y);
		sum[u] = sum[lc[u]] + sum[rc[u]];
	}
	int query(int u, int l, int r, int x, int y){
		if(!u) return 0;
		if(l >= x && r <= y) return sum[u];
		int mid = (l + r) >> 1, ret = 0;
		if(x <= mid) ret += query(lc[u], l, mid, x, y);
		if(y > mid) ret += query(rc[u], mid + 1, r, x, y);
		return ret;
	}
}sg, ch;

struct PointTree{
	int sum, root, mxp[N], siz[N], fa[N], dep[N], dis[N][20];
	bool del[N];
	void dfs(int u, int f){
		siz[u] = 1;
		mxp[u] = 0;
		for(int i = first[u]; i; i = e[i].ne){
			int v = e[i].v;
			if(v == f || del[v]) continue;
			dfs(v, u);
			siz[u] += siz[v];
			mxp[u] = max(mxp[u], siz[v]);
		}
		mxp[u] = max(mxp[u], sum - siz[u]);
		if(mxp[u] < mxp[root]) root = u;
	}
	void getroot(int u, int size){
		mxp[root = 0] = sum = size;
		dfs(u, 0);
		dfs(root, 0);
	}
	void bus(int u, int f, int wc, int d){
		sg.update(sg.root[wc], 0, n, d, w[u]);
		for(int i = first[u]; i; i = e[i].ne){
			int v = e[i].v;
			if(v == f || del[v]) continue;
			bus(v, u, wc, d + 1);
		}
	}
	void buc(int u, int f, int wc, int d){
		ch.update(ch.root[wc], 0, n, d, w[u]);
		for(int i = first[u]; i; i = e[i].ne){
			int v = e[i].v;
			if(v == f || del[v]) continue;
			buc(v, u, wc, d + 1);
		}		
	}
	void build(int u){
		del[u] = 1;
		bus(u, 0, u, 0);
		for(int i = first[u]; i; i = e[i].ne){
			int v = e[i].v;
			if(del[v]) continue;
			getroot(v, siz[v]);
			dep[root] = dep[u] + 1;
			fa[root] = u;
			buc(v, u, root, 1);
			build(root);	
		}
	}
	void init(){
		getroot(1, n);
		build(root);
		lca.ini(root, 0);
		F(i, 1, n) for(int j = i; j; j = fa[j]) dis[i][ dep[i] - dep[j] ] = lca.getdis(i, j);
	}
	void update(int x, int y){
		sg.update(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.update(sg.root[fa[i]], 0, n, d, y - w[x]);
			ch.update(ch.root[i], 0, n, d, y - w[x]);
		}
	}
	int query(int x, int y){
		int ret = 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]] ];
			ret += sg.query(sg.root[fa[i]], 0, n, 0, y - d);
			ret -= ch.query(ch.root[i], 0, n, 0, y - d);
		}
		return ret;
	}
}pt;

signed main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> n >> m;
	F(i, 1, n) cin >> w[i];
	F(i, 1, n - 1){
		int u, v;
		cin >> u >> v;
		add(u, v);
		add(v, u);
	}
	pt.init();
	int las = 0;
	while(m --){
		int op, x, y;
		cin >> op >> x >> y;
		x ^= las;
		y ^= las;
		if(op == 0) las = pt.query(x, y), cout << las << '\n';
		else pt.update(x, y), w[x] = y;
	}
	return 0;
}
posted @   superl61  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示