可持久化线段树算法详解
一、什么是可持久化数据结构
1.1 基本概念
可持久化(Persistent)是指在数据结构被修改后,仍然能够保留修改前的历史版本,并能够在这些历史版本上进行查询操作。
1.2 应用场景
- 区间第k大/小查询(主席树)
- 二维/三维偏序问题
- 版本控制系统
- 时间轴查询问题
二、可持久化线段树(主席树)
2.1 核心思想
- 每次修改只创建新路径上的节点,复用未修改的子树
- 通过根节点数组记录每个版本的入口
2.2 数据结构设计
- struct Node {
- int l, r; // 左右儿子编号
- int val; // 节点存储的值
- // 根据具体问题可添加更多字段
- };
- const int MAXN = 1e5 + 10;
- const int LOG = 20; // log2(MAXN)
- Node tree[MAXN * LOG * 4]; // 节点池
- int roots[MAXN]; // 各个版本的根节点
- int node_cnt = 0; // 节点计数器
复制代码 基础操作实现
3.1 节点创建
- // 创建新节点(可复用旧节点部分信息)
- int new_node(int old = 0) {
- node_cnt++;
- if (old) tree[node_cnt] = tree[old];
- else tree[node_cnt].l = tree[node_cnt].r = tree[node_cnt].val = 0;
- return node_cnt;
- }
复制代码 3.2 建树(初始化版本0)
- int build(int l, int r) {
- int p = new_node();
- if (l == r) {
- // 初始化叶子节点
- return p;
- }
- int mid = (l + r) >> 1;
- tree[p].l = build(l, mid);
- tree[p].r = build(mid + 1, r);
- return p;
- }
复制代码 3.3 单点更新
[code]// 在版本old_root基础上,将位置pos的值增加valint update(int old_root, int l, int r, int pos, int val) { int p = new_node(old_root); if (l == r) { tree[p].val += val; return p; } int mid = (l + r) >> 1; if (pos 1; int left_cnt = tree[tree[root_r].l].val - tree[tree[root_l].l].val; if (k 1; tree[p].l = build(l, mid); tree[p].r = build(mid + 1, r); return p;}int update(int pre, int l, int r, int pos) { int p = ++node_cnt; tree[p] = tree[pre]; tree[p].sum++; if (l == r) return p; int mid = (l + r) >> 1; if (pos > 1; int left_sum = tree[tree[v].l].sum - tree[tree.l].sum; if (k |