树链剖分(树上问题)

树链剖分(树上点权问题)

1、树链剖分之树上线段树:求解路径值,路径大小,路径 \(rmq\),等等 \(...\) (求路径点权之和)。

2、时间复杂度:\(O(nlog^{2}n)\),空间复杂度:\(O(nlogn)\)

3、模版:洛谷P3384,提升:洛谷P10799

template<typename T>
struct X_tree{
	struct Segtree{
		int siz, l, r;
		T lzy, sum;
		Segtree(int siz = 0, int l = 0, int r = 0, T lzy = 0, T sum = 0) : siz(siz), l(l), r(r), lzy(lzy), sum(sum) {}
		#define ls (root << 1)
		#define rs (root << 1 | 1)
		#define ls_ tree[ls]
		#define rs_ tree[rs]
		#define rt_ tree[root]
	};
	int n, m;
	vector<T> a;
	vector<Segtree> tree;
	
	X_tree() {}
	X_tree(int n_) : a(n_ + 1), tree(n_ << 2) {
		n = n_;
		build(1, 1, n);
	}
	
	inline void init() {
		for (int i = 1; i <= n; i++) {
			std::cin >> a[i];
		}
	}
	
	inline Segtree hb(Segtree i, Segtree j) {
		Segtree k;
		k.siz = i.siz + j.siz;
		k.l = i.l;
		k.r = j.r;
		k.sum = i.sum + j.sum;
		k.sum %= mod;
		return k;
	}
	
	inline void push_up(int root) {
		rt_ = hb(ls_, rs_);
	}
	
	inline void build(int root, int l, int r) {
		if (l == r) {
			tree[root] = Segtree(1, l, r, 0, a[l] % mod);
			return;
		}
		int mid = l + r >> 1;
		build(ls, l, mid);
		build(rs, mid + 1, r);
		push_up(root);
	}
	
	inline void push_mark(int root) {
		int l = rt_.l, r = rt_.r;
		if (rt_.lzy) {
			ls_.lzy += rt_.lzy;
			rs_.lzy += rt_.lzy;
			ls_.sum += rt_.lzy * ls_.siz;
			rs_.sum += rt_.lzy * rs_.siz;
			ls_.lzy %= mod;
			ls_.sum %= mod;
			rs_.lzy %= mod;
			rs_.sum %= mod;
			rt_.lzy = 0;
		}
	}
	
	inline Segtree query(int root, int ql, int qr) {
		int l = rt_.l, r = rt_.r;
		if (r < ql || l > qr) {
			return Segtree();
		}
		if (l >= ql && r <= qr) {
			return rt_;
		}
		push_mark(root);
		int mid = l + r >> 1;
		return hb(query(ls, ql, qr), query(rs, ql, qr));
	}
	
	inline void update(int root, int ql, int qr, const T &opt = 1) {
		int l = rt_.l, r = rt_.r;
		if (r < ql || l > qr) {
			return;
		}
		if (l >= ql && r <= qr) {
			rt_.lzy += opt;
			rt_.sum += opt * rt_.siz % mod;
			rt_.lzy %= mod;
			rt_.sum %= mod;
			return;
		}
		push_mark(root);
		int mid = l + r >> 1;
		update(ls, ql, qr, opt);
		update(rs, ql, qr, opt);
		push_up(root);
	}
};

template<typename T>
struct Sp{
	struct node{
		int to, nxt;
		T w;
        node(int to = 0, int nxt = 0, T w = 0) : to(to), nxt(nxt), w(w) {}
	};
	
	int n, m, cnt, root, tot;
	vector<T> W, idw;// 点权、编号对应的点权
	vector<int> id;// 每个节点在树链剖分dfs2遍历序列的最早编号
	vector<int> dep, fa, son, siz, top, head, dfn;
	vector<node> edg;
	X_tree<T> tr;
    Sp() {}
	Sp(int n_, int m_, int root_ = 1)
	: dep(n_ + 1), fa(n_ + 1), son(n_ + 1), 
	siz(n_ + 1), top(n_ + 1), head(n_ + 1), 
	edg(m_ << 1 | 1), dfn(n_ + 1), W(n_ + 1),
	id(n_ + 1), idw(n_ + 1) {
        tot = 0;
		cnt = 0;
		n = n_;
		m = m_;
		root = root_;
		tr = X_tree<T>(n_);
	}
	
	inline void add(int u, int v, const T &w) {
		++cnt;
		edg[cnt].to = v;
		edg[cnt].nxt = head[u];
		edg[cnt].w = w;
		head[u] = cnt;
	}
	
	inline void dfs1(int u, int pa) {
		fa[u] = pa;
		siz[u] = 1;
		dep[u] = dep[pa] + 1;
		for (int i = head[u]; i; i = edg[i].nxt) {
			int v = edg[i].to;
			T w = edg[i].w;
			if (v == pa) {
				continue;
			}
			dfs1(v, u);
			siz[u] += siz[v];
			if (siz[v] > siz[son[u]]) {
				son[u] = v;
			}
		}
	}
	
	inline void dfs2(int u, int topx) {
		top[u] = topx;
		id[u] = ++tot;
		idw[tot] = W[u];
		if (son[u]) {
			dfs2(son[u], topx);
		} else {
			return;
		}
		for (int i = head[u]; i; i = edg[i].nxt) {
			int v = edg[i].to;
			T w = edg[i].w;
			if (v == fa[u] || v == son[u]) {
				continue;
			}
			dfs2(v, v);
		}
	}
	
	inline void init(int n_, int m_, int root_ = 1) {
        tot = 0;
		cnt = 0;
		n = n_;
		m = m_;
		root = root_;
		tr = X_tree<T>(n_);
		dep.assign(n_ + 1, 0);
        fa.assign(n_ + 1, 0);
        son.assign(n_ + 1, 0);
        siz.assign(n_ + 1, 0);
        top.assign(n_ + 1, 0);
        head.assign(n_ + 1, 0);
        edg.assign(m_ << 1 | 1, node());
        dfn.assign(n_ + 1, 0);
        W.assign(n_ + 1, 0);
        id.assign(n_ + 1, 0);
        idw.assign(n_ + 1, 0);
	}

    inline void deal() {// 预处理
        dfs1(root, 0);
		dfs2(root, root);
        for (int i = 1; i <= n; i++) {
            upd_point(i, W[i]);
        }
    }
	
	inline int lca(int x, int y) {
		while (top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) {
				swap(x, y);
			}
			x = fa[top[x]];
		}
		return dep[x] < dep[y] ? x : y;
	}
	
	inline void upd_Range(int x, int y, T k) {// 最短路径上的点权修改
		k %= mod;
		while (top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) {
				swap(x, y);
			}
			int l = id[top[x]], r = id[x];
			tr.update(1, l, r, k);
			x = fa[top[x]];
		}
		if (dep[x] > dep[y]) {
			swap(x, y);
		}
		int l = id[x], r = id[y];
		tr.update(1, l, r, k);
	}
	
	inline void upd_point(int x, T k) {// 单点的点权修改
		tr.update(1, id[x], id[x], k);
	}
	
	inline void upd_tree(int x, T k) {// 以x为根的子树的点权修改
		int l = id[x], r = l + siz[x] - 1;
		tr.update(1, l, r, k);
	}
	
	inline T qur_Range(int x, int y) {// 最短路径的点权之和
		T ans = 0;
		while (top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) {
				swap(x, y);
			}
			int l = id[top[x]], r = id[x];
			ans = ans + tr.query(1, l, r).sum;
			x = fa[top[x]];
		}
		if (dep[x] > dep[y]) {
			swap(x, y);
		}
		int l = id[x], r = id[y];
		ans = ans + tr.query(1, l, r).sum;
		ans %= mod;
		return ans;
	}
	
	inline T qur_point(int x) {// 单点查询
		return tr.query(1, id[x], id[x]).sum;
	}
	
	inline T qur_tree(int x) {// 一颗树的点权之和
		int l = id[x], r = l + siz[x] - 1;
		return tr.query(1, l, r).sum;
	}
};

void solve() {
	int n, m, root;
	std::cin >> n >> m >> root >> mod;
	Sp<i64> tr(n, n - 1, root);
    for (int i = 1; i <= n; i++) {
        std::cin >> tr.W[i];
    }
    for (int i = 1, u, v; i < n; i++) {
        i64 w = 1;
        std::cin >> u >> v;
        tr.add(u, v, w);
        tr.add(v, u, w);
    }
    tr.deal();
	for (int i = 1; i <= m; i++) {
		int opt;
		std::cin >> opt;
		if (opt == 1) {
			int x, y;
			i64 k;
			std::cin >> x >> y >> k;
			tr.upd_Range(x, y, k);
		} else if (opt == 2) {
			int x, y;
			std::cin >> x >> y;
			std::cout << tr.qur_Range(x, y) << '\n';
		} else if (opt == 3) {
			int x;
			i64 k;
			std::cin >> x >> k;
			tr.upd_tree(x, k);
		} else {
			int x;
			std::cin >> x;
			std::cout << tr.qur_tree(x) << '\n';
		}
	}
}

树链剖分(树上边权问题)

1、求边权和求点权是差不多的,无非就是将边的权值赋予给和他相邻的点即可。

2、模版:洛谷P4114洛谷P4116,提升:洛谷树剖边权问题提升题库

template<typename T>
struct X_tree{
	struct Segtree{
		int siz, l, r;
		T mx, sum;
		Segtree(int siz = 0, int l = 0, int r = 0, T mx = 0, T sum = 0) : siz(siz), l(l), r(r), mx(mx), sum(sum) {}
		#define ls (root << 1)
		#define rs (root << 1 | 1)
		#define ls_ tree[ls]
		#define rs_ tree[rs]
		#define rt_ tree[root]
	};
	int n, m;
	vector<T> a;
	vector<Segtree> tree;
	
	X_tree() {}
	
	X_tree(int n_) : a(n_ + 1), tree(n_ << 2) {
		n = n_;
		build(1, 1, n);
	}
	
	inline void init() {
		for (int i = 1; i <= n; i++) {
			std::cin >> a[i];
		}
	}
	
	inline Segtree hb(Segtree i, Segtree j) {
		Segtree k;
		k.siz = i.siz + j.siz;
		k.l = i.l;
		k.r = j.r;
		k.mx = max(i.mx, j.mx);
		k.sum = i.sum + j.sum;
		return k;
	}
	
	inline void push_up(int root) {
		rt_ = hb(ls_, rs_);
	}
	
	inline void build(int root, int l, int r) {
		if (l == r) {
			tree[root] = Segtree(1, l, r, a[l], a[l]);
			return;
		}
		int mid = l + r >> 1;
		build(ls, l, mid);
		build(rs, mid + 1, r);
		push_up(root);
	}
	
	inline void push_mark(int root) {
		int l = rt_.l, r = rt_.r;
	}
	
	inline Segtree query(int root, int ql, int qr) {
		int l = rt_.l, r = rt_.r;
		if (r < ql || l > qr) {
			return Segtree();
		}
		if (l >= ql && r <= qr) {
			return rt_;
		}
		push_mark(root);
		int mid = l + r >> 1;
		return hb(query(ls, ql, qr), query(rs, ql, qr));
	}
	
	inline void update(int root, int ql, int qr, const T &opt = 1) {
		int l = rt_.l, r = rt_.r;
		if (r < ql || l > qr) {
			return;
		}
		if (l >= ql && r <= qr) {
			rt_.mx = opt;
			rt_.sum = opt;
			return;
		}
		push_mark(root);
		int mid = l + r >> 1;
		update(ls, ql, qr, opt);
		update(rs, ql, qr, opt);
		push_up(root);
	}
};

template<typename T>
struct Sp{
	struct node{
		int to, nxt, ids;// ids表示这条边的编号,用于存之后边所连接的下节点
		T w;
        node(int to = 0, int nxt = 0, int ids = 0, T w = 0) : to(to), nxt(nxt), ids(ids), w(w) {}
	};
	
	int n, m, cnt, root, tot;
	vector<T> W, idw;
	vector<int> id;
	vector<int> dep, fa, son, siz, top, head, dfn;
	vector<int> edge;
	vector<node> edg;
	X_tree<T> tr;
	Sp(int n_, int m_, int root_ = 1)
	: dep(n_ + 1), fa(n_ + 1), son(n_ + 1), 
	siz(n_ + 1), top(n_ + 1), head(n_ + 1), 
	edg(m_ << 1 | 1), dfn(n_ + 1), W(n_ + 1),
	id(n_ + 1), idw(n_ + 1), edge(m_ + 1) {
        tot = 0;
		cnt = 0;
		n = n_;
		m = m_;
		root = root_;
		tr = X_tree<T>(n_);
	}
	
	inline void add(int u, int v, const T &w, const int ids = 0) {
		++cnt;
		edg[cnt].to = v;
		edg[cnt].nxt = head[u];
		edg[cnt].w = w;
		edg[cnt].ids = ids;
		head[u] = cnt;
	}

    inline void init(int n_, int m_, int root_ = 1) {
        tot = 0;
		cnt = 0;
		n = n_;
		m = m_;
		root = root_;
		tr = X_tree<T>(n_);
		dep.assign(n_ + 1, 0);
        fa.assign(n_ + 1, 0);
        son.assign(n_ + 1, 0);
        siz.assign(n_ + 1, 0);
        top.assign(n_ + 1, 0);
        head.assign(n_ + 1, 0);
        edg.assign(m_ << 1 | 1, node());
        dfn.assign(n_ + 1, 0);
        W.assign(n_ + 1, 0);
        id.assign(n_ + 1, 0);
        idw.assign(n_ + 1, 0);
        edge.assign(m_ + 1, 0);
	}

    inline void deal() {
        dfs1(root, 0);
		dfs2(root, root);
		W[root] = 0;// 根节点赋值为0
		for (int i = 1; i <= n; i++) {
			upd_point(i, W[i]);
		}
    }
	
	inline void dfs1(int u, int pa) {
		fa[u] = pa;
		siz[u] = 1;
		dep[u] = dep[pa] + 1;
		for (int i = head[u]; i; i = edg[i].nxt) {
			int v = edg[i].to, ids = edg[i].ids;
			T w = edg[i].w;
			if (v == pa) {
				continue;
			}
			W[v] = w;
			edge[ids] = v;
			dfs1(v, u);
			siz[u] += siz[v];
			if (siz[v] > siz[son[u]]) {
				son[u] = v;
			}
		}
	}
	
	inline void dfs2(int u, int topx) {
		top[u] = topx;
		id[u] = ++tot;
		idw[tot] = W[u];
		if (son[u]) {
			dfs2(son[u], topx);
		} else {
			return;
		}
		for (int i = head[u]; i; i = edg[i].nxt) {
			int v = edg[i].to;
			T w = edg[i].w;
			if (v == fa[u] || v == son[u]) {
				continue;
			}
			dfs2(v, v);
		}
	}
	
	inline int lca(int x, int y) {
		while (top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) {
				swap(x, y);
			}
			x = fa[top[x]];
		}
		return dep[x] < dep[y] ? x : y;
	}
	
	inline void upd_Range(int x, int y, T k) {// 最短路径上的区间修改
		while (top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) {
				swap(x, y);
			}
			int l = id[top[x]], r = id[x];
			tr.update(1, l, r, k);
			x = fa[top[x]];
		}
		if (dep[x] > dep[y]) {
			swap(x, y);
		}
		int l = id[x] + 1, r = id[y];
		if (l <= r) tr.update(1, l, r, k);
	}
	
	inline void upd_point(int x, T k) {// 单点修改
		tr.update(1, id[x], id[x], k);
	}
	
	inline T qur_Range(int x, int y) {// 最短路径上的区间查询,查询区间之和
		T ans = 0;
		while (top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) {
				swap(x, y);
			}
			int l = id[top[x]], r = id[x];
			ans = ans + tr.query(1, l, r).sum;
			x = fa[top[x]];
		}
		if (dep[x] > dep[y]) {
			swap(x, y);
		}
		int l = id[x] + 1, r = id[y];
		if (l <= r) ans = ans + tr.query(1, l, r).sum;
		return ans;
	}
	
	inline T qur_max(int x, int y) {// 查询区间MAX
		T ans = 0;
		while (top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) {
				swap(x, y);
			}
			int l = id[top[x]], r = id[x];
			ans = max(ans, tr.query(1, l, r).mx);
			x = fa[top[x]];
		}
		if (dep[x] > dep[y]) {
			swap(x, y);
		}
		int l = id[x] + 1, r = id[y];
		if (l <= r) ans = max(ans, tr.query(1, l, r).mx);
		return ans;
	}
};

void solve() {
	int n;
	std::cin >> n;
	Sp<i64> tr(n, n - 1);
    for (int i = 1, u, v; i < n; i++) {
        i64 w = 1;
        std::cin >> u >> v >> w;
        tr.add(u, v, w, i);
        tr.add(v, u, w, i);
    }
    tr.deal();
	while (1) {
		string opt;
		std::cin >> opt;
		if (opt == "CHANGE") {
			int x;
			i64 k;
			std::cin >> x >> k;
			tr.upd_point(tr.edge[x], k);
		} else if (opt == "QUERY") {
			int x, y;
			std::cin >> x >> y;
            std::cout << tr.qur_max(x, y) << '\n';
		} else {
			break;
		}
	}
}
posted @ 2024-08-29 14:02  grape_king  阅读(22)  评论(0)    收藏  举报