标签:
划分树是保存了快速排序的过程的树,可以用来求静态第k小的数
如果,划分树可以看做是线段树,它的左孩子保存了mid-L+1 个 小于等于 a[mid] 的数字, 右孩子保存了 R-mid个大于等于a[mid]的数字
数组a是排序过后的数组,而划分树保存的是原数组的数据,
划分树的构造就是将上一层[l,r]个数的 mid-l+1个数划分到左子区间,r-(mid-l+1)个数划分到了右子区间
void build(int l, int r, int rt) { if (l == r) return; int mid = (l + r) >> 1, isSame = mid - l + 1; for (int i = l; i <= r; ++i) if (tree[rt][i] < sortA[mid])//mid-l+1个数被划入了左子树,减去比sortA[mid]小的,那么就是记录几个和其相等的被划入左子树 isSame--; int lPos = l, rPos = mid + 1; for (int i = l; i <= r; ++i) { //sum[rt][i] 为前i个数字,有多少个数字被划分到了左区间 i == l ? sum[rt][i] = 0 : sum[rt][i] = sum[rt][i - 1]; if (tree[rt][i] < sortA[mid]) { sum[rt][i]++; tree[rt+1][lPos++] = tree[rt][i]; } else if (tree[rt][i]>sortA[mid]) { tree[rt + 1][rPos++] = tree[rt][i]; } else { if (isSame > 0) { isSame--; sum[rt][i]++; tree[rt + 1][lPos++] = tree[rt][i]; } else { tree[rt + 1][rPos++] = tree[rt][i]; } } } build(l, mid, rt + 1); build(mid + 1, r, rt + 1); }
那么查询区间[l,r]第k大, 就是如果 sum[rt][r] - sum[rt][l-1] 如果>=k, 说明该区间有sum[rt][r] - sum[rt][l-1] 个数被划分到了左子区间,
所以应该去左区间找第k小的数, 否则,去右子区间找第k -( sum[rt][r] - sum[rt][l-1] ) 大的数
int query(int l, int r, int rt, int L, int R, int k) { int mid = (l + r) >> 1; if (l == r) return tree[rt][l]; int s1, s2; if (l == L) { s1 = 0; s2 = sum[rt][R]; } else { s1 = sum[rt][L - 1]; s2 = sum[rt][R] - s1; } //要记住查询的区间,同样也变化了 if (k<=s2) return query(l, mid, rt + 1, l + s1, l + s1 + s2 - 1, k); else return query(mid + 1, r, rt + 1, mid + L - l + 1 - s1, mid - l + 1 - s1 + R - s2 ,k-s2); }
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> #include <iostream> #include <queue> #include <stack> #include <vector> #include <map> #include <set> #include <string> #include <math.h> using namespace std; #pragma warning(disable:4996) #pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; const int INF = 1<<30; /* */ const int N = 100000 + 10; int a[N], sortA[N], tree[20][N],sum[20][N]; void build(int l, int r, int rt) { if (l == r) return; int mid = (l + r) >> 1, isSame = mid - l + 1; for (int i = l; i <= r; ++i) if (tree[rt][i] < sortA[mid])//mid-l+1个数被划入了左子树,减去比sortA[mid]小的,那么就是记录几个和其相等的被划入左子树 isSame--; int lPos = l, rPos = mid + 1; for (int i = l; i <= r; ++i) { //sum[rt][i] 为前i个数字,有多少个数字被划分到了左区间 i == l ? sum[rt][i] = 0 : sum[rt][i] = sum[rt][i - 1]; if (tree[rt][i] < sortA[mid]) { sum[rt][i]++; tree[rt+1][lPos++] = tree[rt][i]; } else if (tree[rt][i]>sortA[mid]) { tree[rt + 1][rPos++] = tree[rt][i]; } else { if (isSame > 0) { isSame--; sum[rt][i]++; tree[rt + 1][lPos++] = tree[rt][i]; } else { tree[rt + 1][rPos++] = tree[rt][i]; } } } build(l, mid, rt + 1); build(mid + 1, r, rt + 1); } int query(int l, int r, int rt, int L, int R, int k) { int mid = (l + r) >> 1; if (l == r) return tree[rt][l]; int s1, s2; if (l == L) { s1 = 0; s2 = sum[rt][R]; } else { s1 = sum[rt][L - 1]; s2 = sum[rt][R] - s1; } //要记住查询的区间,同样也变化了 if (k<=s2) return query(l, mid, rt + 1, l + s1, l + s1 + s2 - 1, k); else return query(mid + 1, r, rt + 1, mid + L - l + 1 - s1, mid - l + 1 - s1 + R - s2 ,k-s2); } int main() { int n, m, k, L, R; int t; while (scanf("%d%d", &n, &m)!=EOF) { for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); tree[0][i] = sortA[i] = a[i]; } sort(sortA, sortA + n + 1); build(1, n, 0); while (m--) { scanf("%d%d%d", &L, &R, &k); printf("%d\n", query(1, n, 0, L, R, k)); } } return 0; }
标签:
原文地址:http://www.cnblogs.com/justPassBy/p/4643976.html