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

关键:

1.离线操作:

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

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

2.本题注意要离散化

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

#include 
#include 
#include 
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;
}


你可能感兴趣的:(ACM_数据结构)