可持久化版本的线段树
支持对历史版本的一些操作
每个版本对应一个根
这样也就相当于每个版本有一棵线段树
有信息被更改的节点就新建一个
没有则与原来的共用
线段树用来维护出现次数,所以一般都用到离散化
有了每个数字出现次数自然就可以查第 k 大了
那么区间第 k 大呢?
其实主席树大概是一个前缀和套线段树的数据结构
这样利用前缀和算出区间内出现次数
也就可以得出区间第 k 大了
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<cmath> using namespace std; const int MAXN = 200001; struct Node{ int ls, rs, sum; Node(){ls = rs = sum = 0;} }t[MAXN << 5]; int n, m, sizb, poolcur; int a[MAXN], b[MAXN], rnk[MAXN], Root[MAXN << 5]; inline int rd() { register int x = 0; register char c = getchar(); register bool f = false; while(!isdigit(c)) { if(c == '-') f = true; c = getchar(); } while(isdigit(c)) { x = x * 10 + c - 48; c = getchar(); } return f ? -x : x; } int build(int l, int r) { int cur = ++poolcur; if(l == r) return cur; int mid = ((l + r) >> 1); t[cur].ls = build(l, mid); t[cur].rs = build(mid + 1, r); return cur; } int Insert(int l, int r, int cur, int dst) { int newcur = ++poolcur; t[newcur] = t[cur]; ++t[newcur].sum; if(l == r) return newcur; int mid = ((l + r) >> 1); if(dst <= mid) t[newcur].ls = Insert(l, mid, t[cur].ls, dst); else t[newcur].rs = Insert(mid + 1, r, t[cur].rs, dst); return newcur; } int query(int x, int y, int l, int r, int k) { if(l == r) return l; int lsiz = t[t[y].ls].sum - t[t[x].ls].sum; int mid = ((l + r) >> 1); if(k <= lsiz) return query(t[x].ls, t[y].ls, l, mid, k); else return query(t[x].rs, t[y].rs, mid + 1, r, k - lsiz); } inline void init() { sort(b + 1, b + n + 1); sizb = unique(b + 1, b + n + 1) - b - 1; for(int i = 1; i <= n; ++i) rnk[i] = lower_bound(b + 1, b + sizb + 1, a[i]) - b; return; } int main() { n = rd(); m = rd(); for(int i = 1; i <= n; ++i) a[i] = b[i] = rd(); init(); Root[0] = build(1, sizb); for(int i = 1; i <= n; ++i) Root[i] = Insert(1, sizb, Root[i - 1], rnk[i]); int l, r, k; while(m--) { l = rd(); r = rd(); k = rd(); printf("%d ", b[query(Root[l - 1], Root[r], 1, sizb, k)]); } return 0; }