传送门:点击打开链接
题意:区间查询第k大
思路:主席树裸题,原来以前看不懂主席树是因为不懂可持久线段树,要学习主席树,应该先学习可持久线段树,然后再学的,这样主席树就能秒懂了。
主席树就是利用可持久线段树维护前缀和,然后利用前缀和就能查询中间的第k大了。
#include<map> #include<set> #include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iomanip> #include<iostream> #include<algorithm> #include<functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int>PII; const int MX = 5e5 + 5; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; int A[MX], B[MX], rear; int S[MX << 2], ls[MX << 2], rs[MX << 2], o[MX], sz; void push_up(int rt) { S[rt] = S[ls[rt]] + S[rs[rt]]; } void build(int l, int r, int &rt) { rt = ++sz; if(l == r) { S[rt] = 0; return; } int m = (l + r) >> 1; build(l, m, ls[rt]); build(m + 1, r, rs[rt]); push_up(rt); } void update(int pos, int l, int r, int pre, int &rt) { rt = ++sz; if(l == r) { S[rt] = S[pre] + 1; return; } int m = (l + r) >> 1; ls[rt] = ls[pre]; rs[rt] = rs[pre]; if(pos <= m) update(pos, l, m, ls[pre], ls[rt]); else update(pos, m + 1, r, rs[pre], rs[rt]); push_up(rt); } int query(int k, int l, int r, int pre, int rt) { if(l == r) return l; int m = (l + r) >> 1, num = S[ls[rt]] - S[ls[pre]]; if(k <= num) return query(k, l, m, ls[pre], ls[rt]); else return query(k - num, m + 1, r, rs[pre], rs[rt]); } int main() { int T, n, m; //FIN; scanf("%d", &T); while(T--) { sz = 0; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); B[i] = A[i]; } sort(B + 1, B + 1 + n); rear = unique(B + 1, B + 1 + n) - B - 1; build(1, n, o[0]); for(int i = 1; i <= n; i++) { int id = lower_bound(B + 1, B + 1 + rear, A[i]) - B; update(id, 1, rear, o[i - 1], o[i]); } for(int i = 1; i <= m; i++) { int l, r, k; scanf("%d%d%d", &l, &r, &k); int id = query(k, 1, rear, o[l - 1], o[r]); printf("%d\n", B[id]); } } return 0; }