BZOJ3631(树链剖分)

本文详细介绍了一道经典的树链剖分题目实现方法,通过树链剖分算法优化查询操作,利用离线处理技巧实现高效的路径覆盖更新。文章提供了一个完整的C++实现代码示例,包括节点深度、子树大小等关键数据结构的设计与维护。

差不多可以说是树链剖分的模板题了,直接维护即可。


#include <bits/stdc++.h>

using namespace std;

#define REP(i,n)                for(int i(0); i <  (n); ++i)
#define rep(i,a,b)              for(int i(a); i <= (b); ++i)
#define dec(i,a,b)              for(int i(a); i >= (b); --i)
#define for_edge(i,x)           for(int i = H[x]; i; i = X[i])

#define LL      long long
#define ULL     unsigned long long
#define MP      make_pair
#define PB      push_back
#define FI      first
#define SE      second
#define INF     1 << 30


const int N     =    300000      +       10;
const int M     =    10000       +       10;
const int Q     =    1000        +       10;
const int A     =    30          +       1;

int E[N << 1], H[N << 1], X[N << 1];
int c[N];
int top[N];
int fa[N];
int deep[N];
int num[N];
int son[N];
int fp[N];
int p[N];
int et, pos;
int a[N];
int n, x, y;

inline int lowbit(int x){ return (x) & (-x);}

inline int query(int x){int ret = 0; for (; x; x -= lowbit(x)) ret += c[x]; return ret;}
inline void add(int x, int val){ for (; x <= n; x += lowbit(x)) c[x] += val;}

inline void addedge(int a, int b){
	E[++et] = b, X[et] = H[a], H[a] = et;
	E[++et] = a, X[et] = H[b], H[b] = et;
}

void dfs(int x, int pre){
	deep[x] = deep[pre] + 1;
	fa[x] = pre;
	num[x] = 1;
	for_edge(i, x){
		int v = E[i];
		if (v != pre){
			dfs(v, x);
			num[x] += num[v];
			if (son[x] != -1 || num[v] > num[son[x]])
				son[x] = v;
		}
	}
}

void getpos(int x, int sp){
	top[x] = sp;
	p[x] = ++pos;
	fp[p[x]] = x;
	if (son[x] == -1) return;
	getpos(son[x], sp);
	for_edge(i, x){
		int v = E[i];
		if (v != son[x] && v != fa[x]) 
			getpos(v, v);
	}
}

void cover(int u, int v, int val){
	int f1 = top[u], f2 = top[v];
	int tmp = 0;
	while (f1 != f2){
		if (deep[f1] < deep[f2]){
			swap(f1, f2);
			swap(u, v);
		}
		add(p[f1], val);
		add(p[u] + 1, -val);
		u = fa[f1];
		f1 = top[u];
	}

	if (deep[u] > deep[v]) swap(u, v);
	add(p[u], val);
	add(p[v] + 1, -val);
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("test.txt", "r", stdin);
	freopen("test.out", "w", stdout);
#endif

	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", a + i);
	rep(i, 1, n - 1){
		scanf("%d%d", &x, &y);
		addedge(x, y);
	}
	

	memset(son, -1, sizeof son);
	dfs(1, 0);
	getpos(1, 1);	
	rep(i, 1, n - 1){
		x = a[i], y = a[i + 1];
		cover(x, y, 1);
	}

	rep(i, 1, n) if (i == a[1]) printf("%d\n", query(p[i]));
	else printf("%d\n", query(p[i]) - 1);
	

	return 0;

}




### 树链剖分的适用场景与使用方法 树链剖分是一种高效的数据结构,用于处理上的路径查询和修改问题。它通过将分解为若干条不相交的链来优化复杂度,使得许多原本需要 \(O(n)\) 时间的操作可以在 \(O(\log n)\) 时间内完成[^1]。 #### 1. 树链剖分的核心思想 树链剖分的核心在于将分割成若干条链,这些链可以拼接成上的任意路径。常见的树链剖分方法包括重链分和长链分。其中,重链分是最常用的一种方法,其基本原理是:对于每个节点,选择其所有子节点中包含节点数最多的子节点作为重儿子,连接重儿子的边称为重边,由重边构成的链称为重链[^2]。 #### 2. 树链剖分的适用场景 树链剖分适用于以下场景: - **路径查询**:例如,求解上两点之间的最大值、最小值或和等问题。 - **路径修改**:例如,对上某条路径上的所有节点进行加法或乘法操作。 - **子查询**:例如,求解某个节点的子中的最大值、最小值或和等问题。 - **动态维护**:当的结构或节点属性发生变化时,树链剖分结合线段等数据结构可以高效地维护这些变化。 #### 3. 树链剖分的应用方法 以下是树链剖分的基本应用步骤: ```python # 树链剖分的实现示例(Python) from collections import defaultdict, deque class TreeChainDecomposition: def __init__(self, n): self.n = n self.adj = defaultdict(list) self.parent = [0] * (n + 1) self.depth = [0] * (n + 1) self.size = [0] * (n + 1) self.heavy = [0] * (n + 1) self.top = [0] * (n + 1) self.pos = [0] * (n + 1) self.rpos = [0] * (n + 1) self.cnt = 0 def add_edge(self, u, v): self.adj[u].append(v) self.adj[v].append(u) def dfs1(self, u, p): self.parent[u] = p self.size[u] = 1 max_subtree = -1 for v in self.adj[u]: if v != p: self.depth[v] = self.depth[u] + 1 self.dfs1(v, u) self.size[u] += self.size[v] if self.size[v] > max_subtree: max_subtree = self.size[v] self.heavy[u] = v def dfs2(self, u, t): self.top[u] = t self.pos[u] = self.cnt self.rpos[self.cnt] = u self.cnt += 1 if self.heavy[u] != 0: self.dfs2(self.heavy[u], t) for v in self.adj[u]: if v != self.parent[u] and v != self.heavy[u]: self.dfs2(v, v) # 示例:初始化并构建 n = 5 tree = TreeChainDecomposition(n) edges = [(1, 2), (1, 3), (2, 4), (2, 5)] for u, v in edges: tree.add_edge(u, v) tree.dfs1(1, 0) tree.dfs2(1, 1) ``` 上述代码实现了树链剖分的基本框架,包括深度优先搜索(DFS)和重链划分。 #### 4. 实际应用案例 以 bzoj3252 为例,题目要求在状结构中求解路径的最大价值和。这种问题可以通过树链剖分结合线段状数组来解决。具体步骤如下: - 使用树链剖分划分为若干条链。 - 对每条链建立线段或其他支持快速区间查询和修改的数据结构。 - 在查询或修改时,将路径拆分为若干条链,并分别在线段上进行操作[^3]。 #### 5. 注意事项 - 树链剖分的时间复杂度通常为 \(O(n \log n)\),适合处理大规模数据。 - 在实际应用中,需要根据问题的具体需求选择合适的分方式(如重链分或长链分)。 - 结合其他数据结构(如线段状数组)可以进一步提升效率。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值