线段树的高级用法
1. 动态开点及标记永久化
2. 线段树的分裂和合并
3. 李超线段树
4. zkw线段树
5. 树套树
6. 线段树分治
7. 猫树
7.1 引入
对于某一些特殊的题目,我们不需要进行修改操作,我们是否可以对线段树进行一些处理,以此来加速它的查询时间。
7.2 猫树
下面以区间最值为例。
考虑一个询问 \([l, r]\)。如果 \(l = r\),则我们可以直接得出答案。
否则,考虑 \([l, r]\) 在线段树上定位时,是线段树中的哪个节点同时递归到左右两个儿子,我们假设这个点为 \(p\) 中。
7.3 实现
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5, LOG = 20;
int n, m, len = 2;
int a[N + 5];
int s[LOG + 5][(N << 2) + 5], pos[(N << 2) + 5], lg[(N << 2) + 5];
int lc(const int &p) { return p << 1; }
int rc(const int &p) { return p << 1 | 1; }
void build(int p, int l, int r, int dep) {
if (l == r)
return pos[l] = p, void();
int mid = (l + r) >> 1;
build(lc(p), l, mid, dep + 1), build(rc(p), mid + 1, r, dep + 1);
s[dep][mid] = a[mid];
for (int i = mid - 1; i >= l; i--)
s[dep][i] = max(s[dep][i + 1], a[i]);
s[dep][mid + 1] = a[mid + 1];
for (int i = mid + 2; i <= r; i++)
s[dep][i] = max(s[dep][i - 1], a[i]);
}
void init() {
while (len < n)
len <<= 1;
for (int i = 2; i <= (len << 1); i++)
lg[i] = lg[i >> 1] + 1;
build(1, 1, len, 1);
}
int query(int l, int r) {
if (l == r)
return a[l];
int d = lg[pos[l]] - lg[pos[l] ^ pos[r]];
return max(s[d][l], s[d][r]);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
init();
while (m--) {
int l, r;
cin >> l >> r;
cout << query(l, r) << '\n';
}
return 0;
}
7.4 例题
本文来自博客园,作者:zhou_ziyi,转载请注明原文链接:https://www.cnblogs.com/zhouziyi/p/Segment_Tree.html