POJ2528 计算可见线段(线段树)

原题链接:POJ2528

解析:这题考察的是线段树子区间更新的维护中的计算可见线段。

用离散化,排序去重,但是这题广告是一块瓷砖一块瓷砖贴的,也就是说有可能离散化之后,明明俩个相邻点之前有空白,但是由于离散化分配序号是紧挨着的,就造成了俩块有广告瓷砖紧挨。举个例子,比如四号瓷砖(以下用号简称)和五号都有广告,那么离散化之后四号序号为1,五号为2,他们之间没别的位置,故全覆盖,那如果四号和六号有广告,但是五号没有,由于我们离散化记录的是广告左右边,故四号为1,六号为2,那么他们之间还是没空白,这就与题意不符了。所以当相邻边大于1时,序号要多+1。

代码实例:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 10100;
int x[4*maxn];
int id[10000010];
struct CPost{
	int l,r;
	bool vis;
}poster[maxn];
struct CNode{
	int l,r;
	bool bCovered;
	int Mid(){
		return (l+r)/2;
	}
}tree[1000000];
void BuildTree(int root,int l,int r){
	tree[root].l = l;
	tree[root].r = r;
	tree[root].bCovered = false;
	if(l == r)	return;
	BuildTree(2*root+1,l,(l+r)/2);
	BuildTree(2*root+2,(l+r)/2+1,r);
}
bool Push(int root,int s,int e){
	if(tree[root].bCovered)	return false;
	if(s == tree[root].l && e == tree[root].r){
		tree[root].bCovered = true;
		return true;
	}
	bool res;
	if(e <= tree[root].Mid()) res = Push(2*root+1,s,e);
	else if(s > tree[root].Mid())	res = Push(2*root+2,s,e);
	else{
		bool r1 = Push(2*root+1,s,tree[root].Mid());
		bool r2 = Push(2*root+2,tree[root].Mid()+1,e);
		res = r1 || r2;
	}
	if(tree[2*root+1].bCovered && tree[2*root+2].bCovered)
		tree[root].bCovered = true;
	return res;
}

int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		int n,cnt;
		scanf("%d",&n);
		for(int i = 0;i < n;i++){
			scanf("%d%d",&x[i],&x[i+n]);
			poster[i].l = x[i];
			poster[i].r = x[i+n];
		}
		
		sort(x,x+2*n);
		cnt = unique(x,x+2*n)-x;
		int interval = 1;
		for(int i = 0;i < cnt;i++){
			id[x[i]] = interval;
			if(i < cnt-1){
				if(x[i+1] - x[i] == 1)	interval++;
				else interval += 2;
			}
		}
		BuildTree(0,1,interval);
		int ans = 0;
		for(int i = n-1;i >= 0;i--){
			int s = id[poster[i].l];
			int e = id[poster[i].r];
			if(Push(0,s,e))	ans++;
		}
		printf("%d
",ans);
	} 
	return 0;
} 
原文地址:https://www.cnblogs.com/long98/p/10352196.html