P3834 【模板】可持久化线段树 1(主席树)
Description
- 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。
Input
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。
第二行包含N个整数,表示这个序列各项的数字。
接下来M行每行包含三个整数l,r,k , 表示查询区间[l,r]内的第k小值。
Outupt
- 输出包含k行,每行1个整数,依次表示每一次查询的结果
Sample Input
5 5 25957 6405 15770 26287 26465 2 2 1 3 4 1 4 5 1 1 2 2 4 4 1
Sample Output
6405 15770 26287 25957 26287
Data Size
对于100%的数据满足:1≤N,M≤2⋅10^5
对于数列中的所有数ai,均满足−10^9≤ai≤10^9
题解:
- 这题的详细说明可以跳转主席树学习笔记
#include <iostream> #include <cstdio> #include <algorithm> #define N 200005 using namespace std; struct T {int l, r, sum;} t[N << 5]; int n, m, dex, cnt; int a[N], b[N], r[N]; int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();} return x *= f; } int find(int x) { return lower_bound(b + 1, b + 1 + cnt, x) - b; } int build(int l, int r) { int p = ++dex, mid = l + r >> 1; if(l == r) return p; t[p].l = build(l, mid); //注意此处不能将p改成++dex然后删去now变量。 //因为一直往下递归会一直改变dex的值,从而使得给t[p].r赋值时会出错。 t[p].r = build(mid + 1, r); return p; } int upd(int las, int l, int r, int val) { int p = ++dex, mid = l + r >> 1; t[p].l = t[las].l, t[p].r = t[las].r; t[p].sum = t[las].sum + 1; if(l == r) return p; if(val <= mid) t[p].l = upd(t[las].l, l, mid, val); else t[p].r = upd(t[las].r, mid + 1, r, val); return p; } int ask(int u, int v, int l, int r, int rank) { if(l == r) return l; int size = t[t[v].l].sum - t[t[u].l].sum, mid = l + r >> 1; if(rank <= size) return ask(t[u].l, t[v].l, l, mid, rank); else return ask(t[u].r, t[v].r, mid + 1, r, rank - size); } int main() { cin >> n >> m; for(int i = 1; i <= n; i++) a[i] = read(), b[++cnt] = a[i]; sort(b + 1, b + 1 + cnt); cnt = unique(b + 1, b + 1 + cnt) - b - 1; r[0] = build(1, cnt); for(int i = 1; i <= n; i++) r[i] = upd(r[i - 1], 1, cnt, find(a[i])); for(int i = 1; i <= m; i++) { int ll = read(), rr = read(), rank = read(); printf("%d\n", b[ask(r[ll - 1], r[rr], 1, cnt, rank)]); } return 0; }