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\n",ans);
}
return 0;
}