主席树
学主席树静态求区间第k大之前,应先学会普通线段树如何求整个序列第k大
建一棵值域线段树,每位的值为当前值有多少个。 然后类似平衡树一样求第k大
主席树是一颗可持久化线段树。 我们发现每次更新一个点的值, 只会改变那个点到根上路径的所有点, 那我们可以动态开点, 重新连一条链出来。
然后利用前缀和的思想, [1~r]的线段树 对应点权值 减去 [1~l-1] 上的权值
就可以得到[l~r]这段区间的信息
然后就可以求区间第k大了
Code
#include<bits/stdc++.h>
using namespace std;
inline int gi() {
int f = 1, s = 0;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -1, c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-'0', c = getchar();
return f == 1 ? s : -s;
}
const int N = 200010;
struct node {
int v, id;
bool operator < (node z) const{
return v < z.v;
}
}a[N];
int b[N], cnt;
struct tree {
int lc, rc, v;
}t[N*20];
int root[N], ans[N];
void update(int l, int r, int pos, int &now) {
t[++cnt] = t[now];
now = cnt;//这里的now带的是指针, 往下更新时要把它指向更新的儿子
t[now].v++;
if (l == r) return ;
int mid = (l + r) >> 1;
if (pos <= mid)
update(l, mid, pos, t[now].lc);
else update(mid+1, r, pos, t[now].rc);
return ;
}
int query(int l, int r, int rt1, int rt2, int k) {
if (l == r) return l;
int s = t[t[rt2].lc].v - t[t[rt1].lc].v, mid = (l + r) >> 1;
if (s >= k)
return query(l, mid, t[rt1].lc, t[rt2].lc, k);
else return query(mid+1, r, t[rt1].rc, t[rt2].rc, k - s);
}
int main() {
int n = gi(), m = gi();
for (int i = 1; i <= n; i++) {
a[i].v = gi(); a[i].id = i;
}
sort(a+1, a+1+n);
int v = 1;
b[a[1].id] = v; ans[v] = a[1].v;
for (int i = 2; i <= n; i++) {
if (a[i].v != a[i-1].v) v++;
b[a[i].id] = v; ans[v] = a[i].v;
}
for (int i = 1; i <= n; i++) {
root[i] = root[i-1];
update(1, n, b[i], root[i]);
}
while (m--) {
int l = gi(), r = gi(), k = gi();
printf("%d
", ans[query(1, n, root[l-1], root[r], k)]);
}
return 0;
}