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

55 阅读2分钟

POJ2104

多次查询区间第K大

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

#include <cstdio> 
#include <algorithm>
#define N 100007
int 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 100007
int 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]之间有多少个数据放到了左边
		else
			toLeft[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];
			}
			else
				splay[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;
}


\

\

\

\

\