POJ 2528 Mayor's Poster(还是线段树,外加离散化)

本文探讨了如何使用离散化技术和线段树解决海报覆盖问题,详细介绍了离散化过程、线段树构建、更新与查询方法,并分析了算法效率与数据结构优化。

题目意思是在一道墙上贴海报,后面贴的海报可能把前面的覆盖,之后给出一个区间,要输出这个区间内能够看见的海报数目。

乍一看非常像POJ 2777涂颜料那道题目,仔细一想还是有很大的不同。首先海报数量十分巨大,不可能用上位运算,这也意味着每次query都要查询到线段树的叶。其次墙所表示的区间十分大,即时能够被 __int64 表示,过大的区间也很容易造成MLE。所以要用上离散化,将整个区间缩小到相对极小的范围内(其实不知道为啥这叫离散化。。)。

最后的代码AC了,但其实还是有问题,离散化的时候没有处理好,导致区间之间的间隙变成了0。可是还是过了,可能是数据太弱。另外也用了lazy,不过可能即使不用也能过。

#include<cstdio>
#include<set>
#include<algorithm>
#include<iostream>
#define MID(x, y)  (x + y >> 1)
#define L(x) (x) << 1
#define R(x) (x) << 1 | 1

using namespace std;

typedef struct{
	int l, r;
	int post;
	int offset;
} NODE;


typedef struct {
	int val;
	int id;
	bool isLeft;
} MAP;
MAP input[40000];
NODE st[88000];
int Left[20000], Right[20000];
set<int> result;

void build(long long t, int l, int r){
	st[t].l = l;
	st[t].r = r;
	st[t].post = 0;
	st[t].offset = 0;
	if(l == r)
		return;
	else{
		build(L(t), l, MID(l, r));
		build(R(t), MID(l, r) + 1, r);
	}
}

void update(long long t, int l, int r, int p){
	if(st[t].l == l && st[t].r == r){
		st[t].post = p;
		st[t].offset = p;
		return;	
	}
	if(st[t].offset && st[t].l != st[t].r){
		st[L(t)].post = st[t].offset;
		st[R(t)].post = st[t].offset;
		st[L(t)].offset = st[t].offset;
		st[R(t)].offset = st[t].offset;
		st[t].offset = 0;
	}
	st[t].post = -1;
	int mid = MID(st[t].l, st[t].r);
	if(mid >= r){
		update(L(t), l, r, p);
	}else if(mid < l){
		update(R(t), l, r, p);
	}else{
		update(L(t), l, mid, p);
		update(R(t), mid + 1, r, p);
	}
}

void query(int t, int l, int r){
	if(st[t].l == l && st[t].r == r && st[t].post != -1){
		result.insert(st[t].post);
		return;
	}
	if(st[t].offset){
		st[L(t)].post = st[t].offset;
		st[R(t)].post = st[t].offset;
		st[L(t)].offset = st[t].offset;
		st[R(t)].offset = st[t].offset;
		st[t].offset = 0;
	}
	st[t].post = -1;
	int mid = MID(st[t].l, st[t].r);
	if(mid >= r){
		query(L(t), l, r);
	}else if(mid < l){
		query(R(t), l, r);
	}else{
		query(L(t), l, mid);
		query(R(t), mid + 1, r);
	}		
}	

bool cmp(MAP a, MAP b){
	return a.val < b.val;
}

int main(){
	int N;
	scanf("%d", &N);
	while(N--){
		int O;
		scanf("%d", &O);
		for(int i = 1; i <= O; i++){
			int x, y;
			scanf("%d %d", &input[i * 2 - 1].val, &input[i * 2].val);
			input[i * 2 - 1].id = i;
			input[i * 2 - 1].isLeft = true;
			input[i * 2].id = i;
			input[i * 2].isLeft = false;
		}
		sort(input + 1, input + 2 * O + 1, cmp);
	
		int value = input[1].val;
		int t = 1;
		for(int i = 1; i <= 2 * O; i++){
			if(value != input[i].val){
				value = input[i].val;
				t++;
			}
		//	cout << input[i].id << " " << input[i].val << " " << t << endl;
			if(input[i].isLeft){
				Left[input[i].id] = t;
			}else{
				Right[input[i].id] = t;
			}
		}
	//	for(int i = 1; i <= O; i++){
//			cout << Left[i] <<" " << Right[i] << endl;
//		}
		build(1, 1, 20000);
		for(int i = 1; i <= O; i++){
//			cout << Left[i] << Right[i]<<endl;;
			update(1, Left[i], Right[i], i);
		}
		result.clear();
		query(1, 1, 20000);
		result.insert(0);
		result.insert(-1);
		cout << result.size() - 2 << endl;
	}
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值