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