题目大意:给出一个数列,求区间第K小数
分析:划分树。就是基于快排原理的线段树。线段树的每一层都类似于一次快排的结果。
建树时,把小于as[mid]的数分到左边,大于as[mid]的数分到右边,相等的根据情况分到左右两边。同时,用一个sum[d][i]数组记录第d层前i个元素比as[mid]小的个数,以便于之后的查询操作时缩小区间以及k。
查询,代码注释蛮清楚的了。[L, R]表示要查询的区间。
int Query(int d, int l, int r, int L, int R, int k) { int s, ss; //s表示区间[l, L)有多少个元素比as[mid]小,ss表示区间[L, R]有多少个元素比as[mid]小 int mid = (l+r)>>1; if(l == r) return tree[d][l]; if(l == L) s = 0; //特判。因为区间是左闭右开的 else s = sum[d][L-1]; ss = sum[d][R]-s; if(ss >= k) return Query(d+1, l, mid, l+s, l+s+ss-1, k); else return Query(d+1, mid+1, r, mid+1+L-l-s, mid+1+R-l-s-ss, k-ss); //L-l-s表示这次Query的区间之前有多少个大于as[mid]的数分到了右边 } //R-l-s-ss表示这次Query的区间以及它之前共有多少个大于as[mid]的数分到了右边
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 111111; int tree[20][maxn]; //每一层类似于一次快排的结果 int sum[20][maxn]; //sum[d][i]表示第d层前i个元素有多少个比as[mid]小,加上等于as[mid]的元素分到左区间的个数 int a[maxn], as[maxn]; //a数组表示原始数组,as数组表示排序后数组 void Build(int d, int l, int r) { int mid = (l+r)>>1; int lpos = l, rpos = mid+1; //lpos记录放入下一层的左区间的位置,rpos则是记录右区间 int lsame = mid-l+1; //lsame记录有前半个区间有多少个数与as[mid]相等,初始时假设全都相等 for(int i = l; i <= mid; i++) if(as[i] < as[mid]) lsame--; for(int i = l; i <= r; i++) { if(i == l) sum[d][i] = 0; else sum[d][i] = sum[d][i-1]; if(tree[d][i] == as[mid]) { if(lsame) { //lsame>0说明与as[mid]相同的元素还可以分到左区间 lsame--; sum[d][i]++; tree[d+1][lpos++] = tree[d][i]; } else tree[d+1][rpos++] = tree[d][i]; } else if(tree[d][i] < as[mid]) { sum[d][i]++; tree[d+1][lpos++] = tree[d][i]; } else tree[d+1][rpos++] = tree[d][i]; } if(l != r) { Build(d+1, l, mid); Build(d+1, mid+1, r); } } int Query(int d, int l, int r, int L, int R, int k) { int s, ss; //s表示区间[l, L)有多少个元素比as[mid]小,ss表示区间[L, R]有多少个元素比as[mid]小 int mid = (l+r)>>1; if(l == r) return tree[d][l]; if(l == L) s = 0; //特判。因为区间是左闭右开的 else s = sum[d][L-1]; ss = sum[d][R]-s; if(ss >= k) return Query(d+1, l, mid, l+s, l+s+ss-1, k); else return Query(d+1, mid+1, r, mid+1+L-l-s, mid+1+R-l-s-ss, k-ss); //L-l-s表示这次Query的区间之前有多少个大于as[mid]的数分到了右边 } //R-l-s-ss表示这次Query的区间以及它之前共有多少个大于as[mid]的数分到了右边 int main() { int T, n, m; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); tree[0][i] = as[i] = a[i]; } sort(as+1, as+n+1); Build(0, 1, n); while(m--) { int L, R, k; scanf("%d%d%d", &L, &R, &k); printf("%d\n", Query(0, 1, n, L, R, k)); } } return 0; }