主席树模板题。
首先我不认为这是什么很高级的数据结构,如果你已经对线段树掌握的很熟练的话应该不难理解主席树。
正文部分:
主席树一般用来解决静态区间的第\(k\)小问题。
原理:
首先我们可以在纸上画一张空的线段树。然后我们加入某个节点的时候你们就在纸上与这个节点所相关的节点的值就加\(1\)。
比如我们加入了值\(1\),那么受到影响的区间就有:\([1,7],[1,4],[1,2],[1,1]\)
好,当我们把所有的点都加完了之后,接下来就要解决第\(k\)小的问题。
先假设我们查的区间为\([l,r]\)那么我们就将第\(l\)颗线段树与第\(r\)颗线段树相减。其中所有节点所得的差就是某段区间里的值得个数。
然后我们就可以查了。如果某段区间中的值得个数大于\(k\),我们就往左子树走,否则就减掉左子树,然后往右子树走。
My Code:
#include <bits/stdc++.h>
#define il inline
const int MAXN = 2e5 + 10;
using namespace std;
int n,m,i,j,k,cnt;
int a[MAXN],lsh[MAXN];
int num[MAXN << 5],l[MAXN << 5],r[MAXN << 5],sum[MAXN << 5];
template<typename T> il void read(T& res) {res = 0;char c;bool sign = 0;for(c = getchar();!isdigit(c);c = getchar()) sign |= c == '-';for(;isdigit(c);c = getchar()) res = (res << 1) + (res << 3) + (c ^ 48);(sign) && (res = -res);return;
}
int build(int l,int r) {cnt++;int num = cnt;if(l < r) {int mid = l + r >> 1;::l[num] = build(l,mid);::r[num] = build(mid + 1,r);}return num;
}
int modify(int pre,int l,int r,int mn) {cnt++;int num = cnt;::l[num] = ::l[pre];::r[num] = ::r[pre];::sum[num] = ::sum[pre] + 1;if(l < r) {int mid = l + r >> 1;if(mn <= mid) ::l[num] = modify(::l[pre],l,mid,mn);else ::r[num] = modify(::r[pre],mid + 1,r,mn);}return num;
}
int query(int x,int y,int l,int r,int k) {if(l >= r) return l;int tmp = ::sum[::l[y]] - ::sum[::l[x]];int mid = l + r >> 1;if(tmp >= k) return query(::l[x],::l[y],l,mid,k);else return query(::r[x],::r[y],mid + 1,r,k - tmp);
}
int main() {read(n);read(m);for(int i = 1;i <= n;i++) {read(a[i]);lsh[i] = a[i];} sort(lsh + 1,lsh + n + 1);int _n = unique(lsh + 1,lsh + n + 1) - lsh - 1;num[0] = build(1,_n);for(int i = 1;i <= n;i++) {int tmp = lower_bound(lsh + 1,lsh + _n + 1,a[i]) - lsh;num[i] = modify(num[i - 1],1,_n,tmp);}
// for(int i = 0;i <= n;i++) cout << num[i] << ' ';cout << endl;for(int i = 1;i <= m;i++) {int l,r,k;read(l);read(r);read(k);int tmp = query(num[l - 1],num[r],1,_n,k); printf("%d\n",lsh[tmp]);}return 0;
}