查询区间第k大 POJ2104 暴力 or 划分树 or 归并树

来源:互联网 发布:mac开机黑屏文件问号 编辑:程序博客网 时间:2024/05/21 14:01

POJ2104

多次查询区间第K大

法一:一次排序+暴力,6907MS

#include <cstdio> #include <algorithm>#define N 100007int ary[N], id[N];bool cmp(int x, int y) {return ary[x] < ary[y];}int main(){int n, m;while(~scanf("%d%d", &n, &m)) {for (int i = 1; i <= n; ++i) {scanf("%d", ary + i);id[i] = i;}std::stable_sort(id + 1, id + 1 + n, cmp);while(m-- > 0) {int x, y, k;scanf("%d%d%d", &x, &y, &k);for (int i = 1; i <= n; ++i) {k -= (x <= id[i] && id[i] <= y);if (k == 0) {printf("%d\n", ary[id[i]]);break;}}}}return 0;}


划分树解法  954MS (就是快些)静态区间查找第k大


#define _CRT_SECURE_NO_WARNINGS#include <cstdio> #include <algorithm>#include <cstring>#define N 100007int splay[20][N], sorted[N];int toLeft[20][N];//第i层第[1:j]之间有多少个数据放到了左边void build(int lv, int x, int y) {if (x == y) return;int mid = x + y >> 1;int suppose = mid - x + 1; //假设中位数sorted[mid]左边的数全部小于sorted[mid]for (int i = x; i <= y; ++i) {if (splay[lv][i] < sorted[mid])suppose--;}//如果suppose == 1, 则数组中值为sorted[mid]只有一个数int lpos = x, rpos = mid + 1;for (int i = x; i <= y; ++i) {if (i == x)  //预处理,相当于初始化toLeft[lv][i] = 0; //lv层[1:i]之间有多少个数据放到了左边elsetoLeft[lv][i] = toLeft[lv][i - 1];if (splay[lv][i] < sorted[mid]) { //划分到中位数的左边toLeft[lv][i]++;splay[lv + 1][lpos++] = splay[lv][i];}else if (splay[lv][i] > sorted[mid]) { //划分到中位数右边splay[lv + 1][rpos++] = splay[lv][i];}else { //这里suppose大于0的数划分到左边if (suppose != 0) {suppose--;toLeft[lv][i]++;splay[lv + 1][lpos++] = splay[lv][i];}elsesplay[lv + 1][rpos++] = splay[lv][i];}}build(lv + 1, x, mid);build(lv + 1, mid + 1, y);}int query(int lv, int l, int r, int L, int R, int k) {//在[L, R]数据中查询第k大の数据if (l == r) return splay[lv][l]; //该句换为 if(L == R) return splay[lv][L];//也能过,时间不一样。我有点晕了int s;//代表[l, L)之间有所少个元素被分到左边int ss;//[L, R]内将被分到左子树的元素数目int mid = l + r >> 1;if (l == L) {s = 0;ss = toLeft[lv][R];}else {s = toLeft[lv][L - 1];ss = toLeft[lv][R] - s;}int newl, newr;if (k <= ss) { //查询左边newl = l + s;newr = l + s + ss - 1;return query(lv + 1, l, mid, newl, newr, k);}else { //查询右边newl = mid - l + 1 + L - s;newr = mid - l + 1 + R - s - ss;return query(lv + 1, mid + 1, r, newl, newr, k - ss);}}int main(){int n, m;while (~scanf("%d%d", &n, &m)) {memset(splay, 0, sizeof splay);for (int i = 1; i <= n; ++i) {scanf("%d", &splay[0][i]);sorted[i] = splay[0][i];}std::sort(sorted + 1, sorted + 1 + n);build(0, 1, n);while (m-- > 0) {int x, y, k;scanf("%d%d%d", &x, &y, &k);printf("%d\n", query(0, 1, n, x, y, k));}}return 0;}


归并树解法2532MS

#define _CRT_SECURE_NO_WARNINGS#include <cstdio>#include <algorithm>using namespace std;const int N = 100007;int T[20][N], a[N];void build(int lv, int l, int r) {if (l == r) {T[lv][l] = a[l];return;}int mid = l + r >> 1;build(lv + 1, l, mid);build(lv + 1, mid + 1, r);//递归简单吧,下面熟悉的归并排序的合并过程int lpos = l, rpos = mid + 1, cur = l;while (lpos <= mid && rpos <= r) {if (T[lv + 1][lpos] <= T[lv + 1][rpos])T[lv][cur++] = T[lv + 1][lpos++];else T[lv][cur++] = T[lv + 1][rpos++];}while (lpos <= mid) T[lv][cur++] = T[lv + 1][lpos++];while (rpos <= r) T[lv][cur++] = T[lv + 1][rpos++];}int query(int lv, int l, int r, int L, int R, int key) { //返回区间[L,R]中不大于key的数的个数if (R < l || L > r) return 0;if (L <= l && r <= R)return lower_bound(T[lv] + l, T[lv] + r + 1, key) - &T[lv][l];int mid = l + r >> 1;return query(lv + 1, l, mid, L, R, key) + query(lv + 1, mid + 1, r, L, R, key);}int solve(int n, int L, int R, int k) {int low = 1, high = n + 1;while (low + 1 < high) {int mid = low + high >> 1;int cnt = query(0, 1, n, L, R, T[0][mid]);if (cnt <= k) low = mid;else high = mid;}return T[0][low];}int main(){int n, m;memset(T, 0, sizeof T);scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i)scanf("%d", a + i);build(0, 1, n);while (m--) {int L, R, k;scanf("%d%d%d", &L, &R, &k);printf("%d\n", solve(n, L, R, k - 1));}return 0;}






阅读全文
0 0
原创粉丝点击