今天学一下主席树
最简单的就是给你一个序列,询问是给定区间中第k大的数。
看了这篇博客,这个图讲解的真的无敌好懂https://blog.csdn.net/bestFy/article/details/78650360
大概的思路就是先对数据进行离散化,主席树每一个节点都是一颗线段树,储存的信息是插入了第i个点后,主席树的状态。
插入就是对离散化后的数据,当成一颗权值线段树来加值就行了
查询就是一个巧妙的类似前缀和的东西,求一下差就好。
另外如果每一个节点都是一颗严格的线段树,空间就是n^2的,处理办法是变成一个递推的过程,如果和上一次插入的状态相等,就连上去,不等才重新开,这样空间是nlogn的。
说的很粗糙,也没指望给谁讲懂,想学去看那个博客就好。
这个算法还是挺妙的。
int a[maxn], b[maxn];
int L[20*maxn], R[20*maxn];
int T[maxn], sum[20*maxn];
int n, m, q, tot = 0;
inline int build(int l, int r){
int rt = ++tot;
if(l < r){
int mid = (l + r) >> 1;
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
return rt;
}
inline int update(int pre, int l, int r, int x){
int rt = ++tot;
L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre] + 1;
if(l < r){
int mid = (l + r) >> 1;
if(x <= mid) L[rt] = update(L[pre], l, mid, x);
else R[rt] = update(R[pre], mid + 1, r, x);
}
return rt;
}
inline int query(int u, int v, int l, int r, int k){
if(l == r) return l;
int x = sum[L[v]] - sum[L[u]], mid = (l + r) >> 1;
if(x >= k) return query(L[u], L[v], l, mid, k);
else return query(R[u], R[v], mid + 1, r, k - x);
}
int main()
{
int t; scanf("%d", &t);
while(t--){
tot = 0;
scanf("%d %d", &n, &q);
rep(i, 1, n) scanf("%d", a + i), b[i] = a[i];
sort(b + 1, b + n + 1);
m = unique(b + 1, b + n + 1) - b - 1;
T[0] = build(1, m);
rep(i, 1, n){
a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
T[i] = update(T[i-1], 1, m, a[i]);
}
while(q--){
int l, r, k; scanf("%d %d %d", &l, &r, &k);
int p = query(T[l-1], T[r], 1, m, k);
printf("%d\n", b[p]);
}
}
return 0;
}