gym 101964:K - Points and Rectangles(CDQ分治)

题目大意:二维平面上,有 q 个操作,每个操作有两种类型:
1:在(x,y)坐标添加一个点
2:添加一个矩形区域:左下角是(x1,y1),右上角是(x2,y2)
对于每一个操作,输出有多少个pair:(矩形区域,点对) ,点对被包含在矩形区域中。


如果当前操作是添加一个矩形区域,答案 = 前面所有矩形区域的答案之和 + 当前矩形区域包括的点的个数。
如果当前操作是添加一个点,答案 = 前面所有的矩形区域的答案之和 + 当前点被包括在的矩形区域的个数。
实际上就是将答案求和,输出。
需要计算一个点被几个矩形区域包含和一个矩形区域包含几个点,用CDQ分治分类处理,矩形包含点的问题比较好处理,就是CDQ三维偏序裸题。
点被矩形包含要变换一下,因为矩形被拆成了4个点,如果一个点被一个矩形包含,那么这个矩形被拆出来的4个点中只有左下角(x,y) 满足 x < x1,y < y1。(可以在图上画,分类讨论点和矩形的相对位置的情况,寻找特征性质。
根据这个CDQ分治即可。


代码:

#include<bits/stdc++.h>
using namespace std;
#define lowbit(i) (i & (-i))
const int maxn = 1e6 + 10;
typedef long long ll;
int q;
struct node{
	int x,y,op,id;
	node(int xi = 0,int yi = 0,int pi = 0,int di = 0) {
		x = xi; y = yi; op = pi; id = di;
	}
}qes[maxn],tmp[maxn];
int c[maxn],t,p,cnt,top;
ll val[maxn],ans[maxn];
void init() {
	sort(c + 1,c + t + 1);
	p = unique(c + 1,c + t + 1) - c - 1;
}
int Hash(int x) {
	return lower_bound(c + 1,c + p + 1,x) - c;
}
void upd(int p,int v) {
	for(int i = p; i < maxn; i += lowbit(i))
		val[i] += v;
}
int qry(int x) {
	int ans = 0;
	for(int i = x; i; i -= lowbit(i))
		ans += val[i];
	return ans;
}
void cdq(int l,int r) {
	if(l >= r) return;
	int mid = l + r >> 1;
	cdq(l,mid);cdq(mid + 1,r);
	top = 0;
	for(int i = l,j = mid + 1; i <= mid || j <= r;) {
		if(i <= mid && (j > r || qes[i].x <= qes[j].x)) {
			if(qes[i].op == 1) upd(qes[i].y,1);
			tmp[++top] = qes[i++];
		} else {
			if(qes[j].op == 2) {
				ans[qes[j].id] += qry(qes[j].y);
			} else if(qes[j].op == 3) {
				ans[qes[j].id] -= qry(qes[j].y);
			}
			tmp[++top] = qes[j++];
		}
	}
	top = 0;
	for(int i = l; i <= mid; i++)
		if(qes[i].op == 1) upd(qes[i].y,-1);
	for(int i = l,j = mid + 1; i <= mid || j <= r;) {
		if(i <= mid && (j > r || qes[i].x < qes[j].x)) {
			if(qes[i].op == 2) upd(qes[i].y,1);
			else if(qes[i].op == 3) upd(qes[i].y,-1);
			tmp[++top] = qes[i++];
		} else {
			if(qes[j].op == 1) ans[qes[j].id] += qry(qes[j].y - 1);
			tmp[++top] = qes[j++];
		}
	}
	for(int i = l; i <= mid; i++) {
		if(qes[i].op == 2) upd(qes[i].y,-1);
		else if(qes[i].op == 3) upd(qes[i].y,1);
	}
	for(int i = l; i <= r; i++)
		qes[i] = tmp[i - l + 1];
}
int main() {
	scanf("%d",&q);
	for(int i = 1; i <= q; i++) {
		int type,x1,y1,x2,y2;
		scanf("%d",&type);
		if(type == 1) {
			scanf("%d%d",&x1,&y1);
			c[++t] = x1,c[++t] = y1;
			qes[++cnt] = node(x1,y1,1,i);
		} else {
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			qes[++cnt] = node(x1 - 1,y1 - 1,2,i);
			qes[++cnt] = node(x2,y2,2,i);
			qes[++cnt] = node(x1 - 1,y2,3,i);
			qes[++cnt] = node(x2,y1 - 1,3,i);
			c[++t] = x1 - 1,c[++t] = y1 - 1,c[++t] = x2,c[++t] = y2;
		}
	}
	init();
	for(int i = 1; i <= cnt; i++) {
		qes[i].x = Hash(qes[i].x);
		qes[i].y = Hash(qes[i].y);
	}
	cdq(1,cnt);
	for(int i = 1; i <= q; i++) {
		ans[i] += ans[i - 1];
		printf("%lld\n",ans[i]);
	}
	return 0;
}
/*
5
1 2 3
1 2 2
1 3 4
2 1 1 5 5
2 2 2 2 2
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值