主席树是可持久化线段树,可以记录线段树的历史版本。
代码中和线段树不同的是,l,r记录的是左右子树编号,因为普通的线段树版本中,左右子树自然就是o<<1和o<<1|1,但是主席树中并不保证这个特性,所以需要记录一下。
代码是
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int N = 1e5+7; int n, m, cnt, root[N], a[N], x, y, k; // 主席树解决区间第k大的问题 // poj-2104 struct node { int l, r, sum; } T[N*40]; // 左儿子右儿子编号 区间和 T[i]维护前缀1~i vector<int> v; int getid(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; } void update(int l, int r, int &x, int y, int pos) { // sum记录的是l和r之间的数 T[++cnt] = T[y], T[cnt].sum++, x = cnt; if (l == r) return ; int mid = (l+r)>>1; if (mid >= pos) update(l, mid, T[x].l, T[y].l, pos); else update(mid+1, r, T[x].r, T[y].r, pos); } int query(int l, int r, int x, int y, int k) { if (l == r) return l; int mid = (l+r) / 2; int sum = T[T[y].l].sum - T[T[x].l].sum; if (sum >= k) return query(l, mid, T[x].l, T[y].l, k); return query(mid+1, r, T[x].r, T[y].r, k - sum); } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), v.push_back(a[i]); sort(v.begin(), v.end()), v.erase(unique(v.begin(), v.end()), v.end()); for (int i = 1; i <= n; ++i) update(1, n, root[i], root[i-1], getid(a[i])); for (int i = 1; i <= m; ++i) { scanf("%d%d%d", &x, &y, &k); printf("%d ", v[query(1,n,root[x-1],root[y],k)-1]); } return 0; }