树链剖分

ps:好久没写博客了QAQ,今天补一次莫名其妙地模板吧。。

树链剖分

  • NOIP联赛经常涉及,虽然没有指名道姓的说用这种算法,可是用了树上差分就很懵逼了...这还不如用树链剖分呢...因为考试要经常涉及,所以我就简单学习了下。(再次鸣谢各路帮助我学习的大佬ymy)

  • 简单理解下,就是可以对树上一段连续区间进行操作,通过轻重链去剖分,要比直接用(dfs)序的要好,时间效率也比较高的(虽然不太明白怎么证明的),然后剖成一段段区间,对树上路径进行操作时,直接对区间进行操作。

  • 要想弄这个,首先必须学好线段树和树状数组和各种区间维护利器,这个才能将树链剖分的能量发挥出来。

预处理

  1. 首先(DfsInit)这个先处理出(dep)(fa)(size)(son)四个数组。

(fa) 记的是这个节点的父亲节点。

(dep) 为这个点的深度 更新为(dep[u]=dep[fa[u]]+1)

(size) 为这个节点的子树的大小(需要包括自己),更新为 (size[u]=1 + sum _{v} size[v]),其中(v)(u)的儿子们。

(son) 就是这个节点的重儿子,所谓重就是这个点的(size)最大,表达为(size[son[u]]=max{(size[v])})(v)的意义同上。

这个更新过程见程序:

int dep[N], sz[N], son[N], fa[N];
void Dfs_Init(int u, int from) {
	sz[u] = 1;
	dep[u] = dep[from] + 1;
	fa[u] = from;
	for (int i = Head[u]; i; i = Next[i]) {
		int v = to[i];
		if (v == from) continue ;
		Dfs_Init(v, u);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]] ) son[u] = v;
	}
}

进行剖分

  1. 再用一个(DfsPart)再处理出(top)(dfn)(num)三个数组,进行树链剖分。

(dfn)就是记这个点的(dfs)序,作为这个点在链上的位置,记录方式为(dfn[u]=++clk)

(num)就是记录(dfn)对应的点,即(num[dfn[u]]=u),可以求出在链上的点在原来树上的位置

(top)就是记这个点所在重链的开始位置,转移为(top[u]=son[fa[u]]==u?top[fa[u]]:u),意思为如果这个点是它父亲的重儿子,就继承它父亲的(top),否则将自己当成重链的顶端。

程序为:

int top[N], dfn[N], num[N];
void Dfs_Part(int u) {
	static int clk = 0;
	dfn[u] = ++clk;
	num[clk] = u;
	top[u] = son[fa[u]] == u ? top[fa[u]] : u;
	if (son[u]) Dfs_Part(son[u]);
	for (int i = Head[u]; i; i = Next[i]) {
		int v = to[i] ;
		if (v == fa[u] || v == son[u]) continue ;
		Dfs_Part(v);
	}
}

树上操作

  1. 剖完后我们就可以进行树上操作了,主要有两个:

1.对于一个点的子树进行更新和询问;

2.对于任意两个点上的路径进行更新和询问。

第一个操作比较简单,对于(u)的子树,我们只要对([dfn[u],dfn[u]+size[u]-1])这一段连续区间进行更新就行了,因为其(dfs)序是连续的,可以直接修改。

第二个操作有些复杂,需要跳链操作,简单来说就是两个点不断向上跳,直到它们在同一条链上,在此期间,我们不断向上跳时要更新你需要更新的区间。

跳的过程就是,求(Lca),对于两个点的(x)(y),求出(top[x]=u)(top[y]=v),比较(u)(v)的深度,如果(dep[u]<dep[v])就交换一下(u)(v)(x)(y),因为我们是要将深度更大的点跳上去,直到他们到同一条链上(即(top[x]==top[y])),中间我们要更新的区间为([dfn[u],dfn[x]])。最后一下我们还要比较一下(x)(y)的深度,将(dep)小的那个放在前面,即如果(dep[x]>dep[y])(swap) (x)(y)再更新([dfn[x],dfn[y]])

代码如下:

void Lca1(int x, int y, int z) {
	z %= Mod;
	while (top[x] != top[y]) {
		int u = top[x], v = top[y];
		if (dep[u] < dep[v]) {
			swap(u, v);
			swap(x, y);
		}
		ul = dfn[u]; ur = dfn[x]; uv = z;
		Update(1, 1, n);
		x = fa[u];
	}
	if (dep[x] > dep[y]) swap(x, y);
	ul = dfn[x]; ur = dfn[y]; uv = z;
	Update(1, 1, n);
}

这个有道很好的裸题去做 Luogu【P3384】【模板】树链剖分 这个就是将两个修改、两个询问结合在一起,比较好做。

简单地附一个代码算了= = 应该还是比较容易看的……

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
	freopen ("P3384.in", "r", stdin);
	freopen ("P3384.out", "w", stdout);
#endif
}

int n, m, rt, Mod;

const int N = 1e6 + 1e3;
vector<int> G[N];

int dep[N], sz[N], son[N], fa[N];
void Dfs_Init(int u, int from) {
	sz[u] = 1; dep[u] = dep[fa[u] = from] + 1;
	for (int v : G[u]) if (v ^ fa[u]) {
		Dfs_Init(v, u), sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) son[u] = v;
	}
}

int dfn[N], num[N], clk, top[N];
void Dfs_Part(int u) {
	num[dfn[u] = ++ clk] = u;
	top[u] = (son[fa[u]] == u) ? top[fa[u]] : u;
	if (son[u]) Dfs_Part(son[u]);
	for (int v : G[u]) if ((v ^ fa[u]) && (v ^ son[u])) Dfs_Part(v);
}

int val[N];

#define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r
#define Add(o, l, r, v) (sumv[(o)] += 1ll * ((r) - (l) + 1) * (v) % Mod) %= Mod, (tag[(o)] += (v)) %= Mod
struct Segment_Tree {
	int sumv[N << 1], tag[N << 1];

	inline void Push_Down(int o, int l, int r) {
		if (!tag[o]) return ; int ls = o << 1, rs = ls | 1, mid = (l + r) >> 1;
		Add(ls, l, mid, tag[o]); Add(rs, mid + 1, r, tag[o]); tag[o] = 0;
	}

	inline void Push_Up(int o) { sumv[o] = sumv[o << 1] + sumv[o << 1 | 1]; }

	void Update(int o, int l, int r, int ul, int ur, int uv) {
		if (ul <= l && r <= ur) { Add(o, l, r, uv); return ; } int mid = (l + r) >> 1; Push_Down(o, l, r);
		if (ul <= mid) Update(lson, ul, ur, uv); if (ur > mid) Update(rson, ul, ur, uv); Push_Up(o);
	}

	int Query(int o, int l, int r, int ql, int qr) {
		if (ql <= l && r <= qr) {
			return sumv[o];
		}
		int mid = (l + r) >> 1, res = 0; Push_Down(o, l, r);
		if (ql <= mid) res += Query(lson, ql, qr);
		if (qr > mid) res += Query(rson, ql, qr); Push_Up(o); return res % Mod;
	}

	void Build(int o, int l, int r) {
		if (l == r) { sumv[o] = val[num[l]]; return ; }
		int mid = (l + r) >> 1; Build(lson); Build(rson); Push_Up(o);
	}
} T;

void Update(int x, int y, int z) {
	while (top[x] ^ top[y]) {
		int u = top[x], v = top[y];
		if (dep[u] < dep[v]) swap(u, v), swap(x, y);
		T.Update(1, 1, n, dfn[u], dfn[x], z); x = fa[u];
	}
	if (dep[x] > dep[y]) swap(x, y);
	T.Update(1, 1, n, dfn[x], dfn[y], z);
}

int Query(int x, int y) {
	int res = 0;
	while (top[x] ^ top[y]) {
		int u = top[x], v = top[y];
		if (dep[u] < dep[v]) swap(u, v), swap(x, y);
		(res += T.Query(1, 1, n, dfn[u], dfn[x])) %= Mod; x = fa[u];
	}
	if (dep[x] > dep[y]) swap(x, y);
	(res += T.Query(1, 1, n, dfn[x], dfn[y])) %= Mod; return res;
}

int main () {
	File();
	n = read(); m = read(); rt = read(); Mod = read();
	For (i, 1, n) val[i] = read();
	For (i, 1, n - 1) {
		int u = read(), v = read();
		G[u].push_back(v);
		G[v].push_back(u);
	}
	Dfs_Init(rt, 0); Dfs_Part(rt); T.Build(1, 1, n);
	For (i, 1, m) {
		int opt = read();
		if (opt == 1) { int x = read(), y = read(), z = read(); Update(x, y, z); }
		else if (opt == 2) { int x = read(), y = read(); printf ("%d
", Query(x, y)); }
		else if (opt == 3) { int x = read(), z = read(); T.Update(1, 1, n, dfn[x], dfn[x] + sz[x] - 1, z); }
		else if (opt == 4) { int x = read(); printf ("%d
", T.Query(1, 1, n, dfn[x], dfn[x] + sz[x] - 1)); }
	}
    return 0;
}

  • 一些题目:
    POJ3237
    HDU2801
    SDOI2017
原文地址:https://www.cnblogs.com/zjp-shadow/p/7750046.html