【Coel.算法笔记】【主席树入门】【模板】可持久化线段树 2
题前闲话
Sherlockk 大佬又开比赛了,然而不会做。
有空切水题还不如多学点东西(暴论)
可持久化线段树 2
可持久化线段树的题目有两个,其中第二个的应用场景更广,所以讲这个。
洛谷传送门
题目描述
如题,给定 个整数构成的序列 ,将对于指定的闭区间 查询其区间内的第 小值。
输入格式
第一行包含两个整数,分别表示序列的长度 和查询的个数 。
第二行包含 个整数,第 个整数表示序列的第 个元素 。
接下来 行每行包含三个整数 , 表示查询区间 内的第 小值。
输出格式
对于每次询问,输出一行一个整数表示答案。
基本思路
本题为主席树的一个应用:静态查询第 小值。
顺便说一句:主席树这个名字来源于它的发明者黄嘉泰的名字缩写 HJT。由此可见这个数据结构已经是十几年前的产物了
联想到之前学过的权值线段树,我们有一个很直观的想法:
开设 个权值线段树,其中 号权值线段树存放区间 中各数字的出现情况。由于权值线段树满足可加减性,所以区间 可以通过 与 合并得来。这样,我们就可以同时查询两个权值树,通过递归寻找到适合的数实现查询。
但这样做空间消耗十分巨大,所以我们采用一个特殊方法:动态开点。
举一个很显然的例子:假设我们要修改的结点位于左子树,那么右子树是完全不会发生变化的。也就是说,我们只需要给左子树开设节点,而右子树直接指向先前的右子树即可。
借用一下 OI-Wiki 的图:
红色为修改后受到影响的结点,白色为旧结点。
代码实现
先对原数组做一个离散化,否则动态开点也还是会爆内存。
void init_hash() {
memcpy(t, a, sizeof(a));
sort(t + 1, t + n + 1);
top = unique(t + 1, t + n + 1) - t - 1;
for (int i = 1; i <= n; i++)
a[i] = lower_bound(t + 1, t + top + 1, a[i]) - t;
}
然后对每个节点做修改操作。传入时要提供先前操作的树,方便指向。
void pushup(int id) {
s[id].v = s[s[id].ls].v + s[s[id].rs].v;
}
void modify(int lsid, int& id, int l, int r, int k, int v) {
if (!id) //到底了,分配新结点
id = ++cnt;
if (l == r)
return s[id].v += v, (void)Miolic;
int mid = (l + r) >> 1;
if (k <= mid) { //权值在左,指向右儿子并修改左儿子
s[id].rs = s[lsid].rs;
s[id].ls = ++cnt;
s[s[id].ls] = s[s[lsid].ls];
modify(s[lsid].ls, s[id].ls, l, mid, k, v);
} else { //权值在右,指向左儿子并修改右儿子
s[id].ls = s[lsid].ls;
s[id].rs = ++cnt;
s[s[id].rs] = s[s[lsid].rs];
modify(s[lsid].rs, s[id].rs, mid + 1, r, k, v);
}
pushup(id);//标记上传
}
最后查询。
nt query(int id1, int id2, int l, int r, int k) {
if (l == r)//到底了,返回
return l;
int mid = (l + r) >> 1, tp = s[s[id2].ls].v - s[s[id1].ls].v;//tp 为当前小于等于 mid 的元素个数
if (tp >= k)//大了往左找
return query(s[id1].ls, s[id2].ls, l, mid, k);
else//小了往右找,还得像平衡树一样减去当前数字
return query(s[id1].rs, s[id2].rs, mid + 1, r, k - tp);
}
代码如下:
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
namespace __Coel_FastIO {
#ifndef LOCAL
#define _getchar_nolock getchar_unlocked
#define _putchar_nolock putchar_unlocked
#endif
int read() {
int x = 0, f = 1;
char ch = _getchar_nolock();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = _getchar_nolock();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = _getchar_nolock();
}
return x * f;
}
void write(int x, char Ctr_sign) {
if (x < 0) {
x = -x;
putchar('-');
}
static int buf[35];
int top = 0;
do {
buf[top++] = x % 10;
x /= 10;
} while (x);
while (top)
_putchar_nolock(buf[--top] + '0');
_putchar_nolock(Ctr_sign);
}
} // namespace __Coel_FastIO
using namespace __Coel_FastIO;
using namespace std;
#define Miolic 0
const int maxn = 1e5 + 10;
struct node {
int v, ls, rs;
} s[maxn << 8];
int root[maxn << 2], cnt;
int n = read(), m = read(), top, a[maxn << 2];
int t[maxn << 2];
void pushup(int id) {
s[id].v = s[s[id].ls].v + s[s[id].rs].v;
}
void modify(int lsid, int& id, int l, int r, int k, int v) {
if (!id)
id = ++cnt;
if (l == r)
return s[id].v += v, (void)Miolic;
int mid = (l + r) >> 1;
if (k <= mid) {
s[id].rs = s[lsid].rs;
s[id].ls = ++cnt;
s[s[id].ls] = s[s[lsid].ls];
modify(s[lsid].ls, s[id].ls, l, mid, k, v);
} else {
s[id].ls = s[lsid].ls;
s[id].rs = ++cnt;
s[s[id].rs] = s[s[lsid].rs];
modify(s[lsid].rs, s[id].rs, mid + 1, r, k, v);
}
pushup(id);
}
int query(int id1, int id2, int l, int r, int k) {
if (l == r)
return l;
int mid = (l + r) >> 1, tp = s[s[id2].ls].v - s[s[id1].ls].v;
if (tp >= k)
return query(s[id1].ls, s[id2].ls, l, mid, k);
else
return query(s[id1].rs, s[id2].rs, mid + 1, r, k - tp);
}
void init_hash() {
memcpy(t, a, sizeof(a));
sort(t + 1, t + n + 1);
top = unique(t + 1, t + n + 1) - t - 1;
for (int i = 1; i <= n; i++)
a[i] = lower_bound(t + 1, t + top + 1, a[i]) - t;
}
int main(void) {
for (int i = 1; i <= n; i++)
a[i] = read();
init_hash();
for (int i = 1; i <= n; i++)
modify(root[i - 1], root[i], 1, top, a[i], 1);
for (int i = 1; i <= m; i++) {
int l = read(), r = read(), k = read();
write(t[query(root[l - 1], root[r], 1, top, k)], '\n');
}
return 0;
}
本文作者:Coel's Blog
本文链接:https://www.cnblogs.com/Coel-Flannette/p/16339929.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步