解题报告:P3834 模板可持久化线段树 2(主席树)详解
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解题报告:P3834 模板可持久化线段树 2(主席树)详解相关的知识,希望对你有一定的参考价值。
P3834 【模板】可持久化线段树 2(主席树)
题解 P3834 【【模板】可持久化线段树 2(主席树)】
1)静态求第k大数
可持久化线段树,不能用堆的方法存子结点了,所以用指针l表示左儿子r表示右儿子
可持久化线段树难以处理区间修改,因为很难处理懒惰标记,除非使用永久化标记线段树
新版本其他的都不变,只把新的点替换,其他的性质例如左右儿子是谁不变,所以每新加入一个数,也就是到了新版本,就完成与一次新老版本的迭代(tr[q] = tr[p]
q
是新版本p
是老版本)
线段树维护的是整个值域。
在数值上建立一个线段树,维护cnt
表示每个数值的区间上一共有几个数。
线段树和平衡树都是二叉树,所以我们需要做二分的时候可以考虑能否在树里做二分。
我们首先考虑[1,R]
这个区间的个数。很明显我们可以直接用第R个版本即可。
但是我们要求的是[L,R]
这个区间,有两个限制。所以我们利用类似前缀和的思想,用第R
个版本个数cnt1
减去第L-1
个版本cnt2
即可得到L
到R
之间的个数:cnt1-cnt2
每次都是个上一个版本比较
l指的是左儿子,不是左边界
由于数据范围达到了1e9
所以需要离散化
//线段树维护的是数值
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N = 500007, M = 10007, INF = 0x3f3f3f3f;
int n, m;
int idx;
int a[N];
int root[N];
vector<int> nums;
struct Tree
int l, r;
int cnt;//表示的是这个值域区间一共有多少个数
tr[N * 4 + N * 17];//log(1e5) ≈ 17
int find(int x)
return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
int build(int l, int r)
int p = ++ idx;
if(l == r)return p;
int mid = l + r >> 1;
tr[p].l = build(l, mid);
tr[p].r = build(mid + 1, r);
return p;
int insert(int p, int l, int r, int x)//开始把n个数插入(迭代新版本)
int q = ++ idx;
tr[q] = tr[p];//新老版本交接
if(l == r)
tr[q].cnt ++ ;
return q;
int mid = l + r >> 1;
if(x <= mid)tr[q].l = insert(tr[p].l, l, mid, x);
else tr[q].r = insert(tr[p].r, mid + 1, r, x);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt ;
return q;
int query(int q, int p, int l, int r, int k)
if(l == r)return r;
int mid = l + r >> 1;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
if(k <= cnt)return query(tr[q].l, tr[p].l, l, mid, k);
else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
//这里注意往右区间找的时候第k大已经变成了k-cnt大了
int main()
scanf("%d%d", &n, &m);
for(int i = 1;i <= n; ++ i)
scanf("%d", &a[i]);
nums.push_back(a[i]);
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
//第一个版本
root[0] = build(0, nums.size() - 1);
//新版本是由旧版本继承过来的
for(int i = 1;i <= n; ++ i)
root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i]));
while(m -- )
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\\n", nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)]);
return 0;
以上是关于解题报告:P3834 模板可持久化线段树 2(主席树)详解的主要内容,如果未能解决你的问题,请参考以下文章