1.定义
主席树又称可持久化线段树,能够保存线段树的历史版本。之所以命名为主席树是因为其发明人黄嘉泰。据说是他在考场上写出来的,,tql。。
学习主席树,需要对线段树有一定了解,线段树请看线段树算法讲解
2.具体实现
主席树的节点的子节点不再是类似于线段树,不再是2*x和2 * x +1.
需要在结构体里面存其子节点,并且,每更新一次都需要新建一个根,所以还需要一个roots数组来记录根。
const int maxn = 1e5 + 10;
struct node{
int l, r;//左子树,右子树
int data;
node()
{
data = 0;
}
}tree[maxn * 20];
int roots[maxn];
int tot = 1;
首先是建树。
以求给定区间内第k小的数为例。
建树类似于线段树的单点修改。
void update(int num, int &root, int l, int r)//对root进行引用&是其精髓所在
{
tree[tot++] = tree[root];//新建一个节点,将其赋值为上一个根的内容
root = tot - 1;//将当前根赋值为新建节点
tree[root].data++;//更新新建的根
if(l == r)//找到叶子节点
return;
int mid = (l + r) >> 1;
if(mid >= num)//新插入的值在左子树内
update(num, tree[root].l, l, mid);
else//在右子树内
update(num, tree[root].r, mid + 1, r);
}
查询区间第k小
int query(int pre, int cur, int k, int l, int r)
{
if(l == r)//找到第k小离散化后的值
return l;
int mid = (l + r) >> 1;
int d = tree[tree[cur].l].data - tree[tree[pre].l].data;//求[l,r]区间内左子树的值
if(d >= k)//若其值大于等于k,说明要找的数在左子树内
return query(tree[pre].l, tree[cur].l, k, l, mid);
else//否则在右子树内,注意k - d
return query(tree[pre].r, tree[cur].r, k - d, mid + 1, r);
}
完整代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 1e5 + 10;
struct node{
int l, r;//左子树,右子树
int data;
node()
{
data = 0;
}
}tree[maxn * 20];
struct V{
int data;
int id;
}value[maxn];
int roots[maxn];
int ran[maxn];
int tot = 1;
bool com(V x, V y)
{
return x.data < y.data;
}
void update(int num, int &root, int l, int r)//对root进行引用&是其精髓所在
{
tree[tot++] = tree[root];//新建一个节点,将其赋值为上一个根的内容
root = tot - 1;//将当前根赋值为新建节点
tree[root].data++;//更新新建的根
if(l == r)//找到叶子节点
return;
int mid = (l + r) >> 1;
if(mid >= num)//新插入的值在左子树内
update(num, tree[root].l, l, mid);
else//在右子树内
update(num, tree[root].r, mid + 1, r);
}
int query(int pre, int cur, int k, int l, int r)
{
if(l == r)//找到第k小离散化后的值
return l;
int mid = (l + r) >> 1;
int d = tree[tree[cur].l].data - tree[tree[pre].l].data;//求[l,r]区间内左子树的值
if(d >= k)//若其值大于等于k,说明要找的数在左子树内
return query(tree[pre].l, tree[cur].l, k, l, mid);
else//否则在右子树内,注意k - d
return query(tree[pre].r, tree[cur].r, k - d, mid + 1, r);
}
void init()
{
mem(ran, 0);
mem(tree, 0);
mem(roots, 0);
tot = 1;
}
int main()
{
int t, n, m;
scanf("%d", &t);
while(t--)
{
init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)//离散化
{
scanf("%d", &value[i].data);
value[i].id = i;
}
sort(value + 1, value + n + 1, com);
for(int i = 1; i <= n; i++)
{
ran[value[i].id] = i;
}
for(int i = 1; i <= n; i++)
{
roots[i] = roots[i - 1];
update(ran[i], roots[i], 1, n);
}
for(int i = 1; i <= m; i++)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
printf("%d\n", value[query(roots[x - 1], roots[y], z, 1, n)].data);//
}
}
return 0;
}