POJ 2761 树状数组+二分+离线

本文介绍了一种离线处理区间查询的方法,并通过二分搜索实现第k小元素的查找。采用离散化手段优化数据处理过程,适用于批量查询场景。通过具体代码示例详细解释了算法的实现细节。

关键:

1.离线操作:

将询问区间从左到右排序,然后依次插入区间内的值,在处理下一个询问时,删去上个询问插入的但当前询问不需要插入的数。

m次询问总体复杂度为 2nlog(n). 

2.本题注意要离散化

3.二分(手写upper_bound)来查询第k小的数

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100005
int a[maxn], b[maxn], c[maxn], p[maxn];
int n, m;
struct Q {
	int l, r, k, id, ans;
} q[maxn >> 1];

int cmp1(Q a, Q b) {
	return a.l < b.l || (a.l == b.l && a.r < b.r);
}
int cmp2(Q a, Q b) {
	return a.id < b.id;
}
void add(int x, int v) {
	for( ; x <= n ; x += x&-x)
		c[x] += v;
}
int sum(int x) {
	int s = 0;
	for( ;x > 0; x -= x&-x)
		s += c[x];
	return s;
}

int bin(int key) // upper_bound
		{
	int l = 1, r = n;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(sum(mid) < key)
			l = mid + 1;
		else
			r = mid - 1;
	}
	return l;
}
int main() {
	int i, j;
	while(~scanf("%d%d", &n, &m)) {
		for(i = 1; i <= n; i++)
			scanf("%d", &a[i]), b[i] = a[i];
		sort(b + 1, b + n + 1);
		int len = unique(b + 1, b + n + 1) - b - 1;
		memset(c, 0, sizeof(c));
		for(i = 0; i < m; i++)
			scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].k), q[i].id = i;
		sort(q, q + m, cmp1);
		for(i = 0; i < m; i++) {
			int l, r;
			if(!i)
				l = q[i].l, r = q[i].r;
			else
				l = q[i - 1].r, r = q[i].r;
			for(j = l; j <= r; j++) {
				p[j] = lower_bound(b + 1, b + len + 1, a[j]) - b;
				add(p[j], 1);
			}
			if(i) {
				for(j = q[i - 1].l; j <= q[i].l - 1; j++)
					add(p[j], -1);
				for(j = q[i].r + 1; j <= q[i - 1].r; j++)
					add(p[j], -1);
			}
			q[i].ans = b[bin(q[i].k)];
		}
		sort(q, q + m, cmp2);
		for(i = 0; i < m; i++)
			printf("%d\n", q[i].ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值