题意:给出一个序列,若干查询,询问区间l到r的第k小值。
解法:划分树可以最有效的解决这个问题……但是划分树只能解决这个问题= =主席树的应用范围比较广……所以只学了主席树……嗯……
膜拜发明主席树的大神,引用一下他的话
..这个东西是当初我弱不会划分树的时候写出来替代的一个玩意..被一小撮别有用心的人取了很奇怪的名字> <
想法是对原序列的每一个前缀[1..i]建立出一颗线段树维护值域上每个数的出现次数,然后发现这样的树是可以减的,然后就没有然后了
通过这位大神的博客学习了原理http://blog.csdn.net/metalseed/article/details/8045038
通过这位大神的博客学习了代码http://www.cnblogs.com/Rlemon/archive/2013/05/23/3094635.html
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #define LL long long #define MAXN 100010 using namespace std; struct node { int sum; int ls, rs; node() {sum = 0, ls = 0, rs = 0;} }T[MAXN * 20]; int a[MAXN], b[MAXN], p[MAXN];//a是原数组,p代表a数组下标,b是离散化后数组 int root[MAXN];//记录每个结点在T中的下标 int sz;//当前数组使用长度 void ins(int &i, int l, int r, int x) { T[++sz] = T[i];//复制上一节点作为新结点 i = sz;//更新当前结点下标 T[i].sum++;//更新线段树维护的和 if(l == r) return ; int m = (l + r) >> 1; if(x <= m) ins(T[i].ls, l, m, x);//更改左儿子 else ins(T[i].rs, m + 1, r, x);//更改右儿子 } int query(int i, int j, int l, int r, int k) { if(l == r) return l; int t = T[T[j].ls].sum - T[T[i].ls].sum;//r线段树减去l-1的线段树值 int m = (l + r) >> 1; if(t >= k) return query(T[i].ls, T[j].ls, l, m, k); else return query(T[i].rs, T[j].rs, m + 1, r, k - t); } bool cmp(int i, int j) { return a[i] < a[j]; } int main() { int n, m; while(~scanf("%d%d", &n, &m)) { for(int i = 1; i <= n; i++) scanf("%d", &a[i]), p[i] = i; sort(p + 1, p + n + 1, cmp);//按原数组大小对下标排序 for(int i = 1; i <= n; i++)//离散化 b[p[i]] = i; sz = 0; root[0] = 0; for(int i = 1; i <= n; i++) { root[i] = root[i - 1]; ins(root[i], 1, n, b[i]); } while(m--) { int l, r, k; scanf("%d%d%d", &l, &r, &k); int t = query(root[l - 1], root[r], 1, n, k); printf("%d ", a[p[t]]); } } return 0; }