POJ 3109 BIT + 坐标离散化 + 扫描线

题意

传送门 POJ 3109

题解

被涂黑的点坐标值不可能超过已涂黑点的坐标极值,故该过程最终会停止。对 x x x 方向扫描线,用树状数组维护 y y y 方向不同位置满足 h o r i z o n t a l − i n n e r horizontal-inner horizontalinner 的点数,当出现满足 v e r t i c a l − i n n e r vertical-inner verticalinner 的成对的顶点时,将其 x x x 坐标范围内的可涂黑点数加入答案。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#define abs(x)    ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-5
#define PI 3.14159265358979323846
#define MAX_N 100005
using namespace std;
typedef long long LL;
int N, H, W;
int X[MAX_N], Y[MAX_N], rec[MAX_N];
vector<int> xs[MAX_N];
int bit[MAX_N];
// BIT
int sum(int i){
	int s = 0;
	while(i > 0){
		s += bit[i];
		i -= i & -i;
	}
	return s;
}

void add(int i, int x){
	while(i <= W){
		bit[i] += x;
		i += i & -i;
	}
}

int compress(int *x){
	vector<int> xs(N);
	for(int i = 0; i < N; i++) xs[i] = x[i];
	sort(xs.begin(), xs.end());
	xs.erase(unique(xs.begin(), xs.end()), xs.end());
	// [1, W]
	for(int i = 0; i < N; i++) x[i] = 1 + distance(xs.begin(), lower_bound(xs.begin(), xs.end(), x[i]));
	return xs.size();
}

int main(){
	while(~scanf("%d", &N)){
		memset(bit, 0, sizeof(bit));
		memset(rec, 0, sizeof(rec));
		for(int i = 0; i < N; i++) scanf("%d%d", X + i, Y + i);
		// 坐标离散化
		H = compress(X);
		W = compress(Y);
		for(int i = 0; i < N; i++) xs[X[i]].push_back(Y[i]);
		LL res = N;
		// 扫描线法
		for(int x = 1; x <= H; x++){
			sort(xs[x].begin(), xs[x].end());
			int pre = -1;
			for(int i = 0; i < xs[x].size(); i++){
				int y = xs[x][i], s = sum(y);
				if(rec[y] == 1) res += s;
				add(y, -s);
				add(y + 1, s);
				if(pre != -1){
					add(pre + 1, 1);
					add(y, -1);
				}
				// 记录 x 方向是否已出现前驱节点,y 方向前驱节点
				rec[y] = 1;
				pre = y;
			}
		}
		printf("%lld\n", res);
	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值