题目链接如下:
http://acm.hdu.edu.cn/showproblem.php?pid=1542
这是一道扫描线和线段树结合的题目。
我们把区间看作子节点,用一个数组存储横坐标,排序去重,就得到了可以维护的区间。
对所有横边按照高排序,我们可以得到每一次扫描线需要更新的顺序。
参考:
https://blog.youkuaiyun.com/u013480600/article/details/22548393
在线段树里面,设置一个cnt用于区间的标记更新,用sum记录区间内有多长的边。
这里说一下cnt=-1的设置,这个-1代表的是其左右区间中存在被上下边覆盖次数不一致的区间。
我一开始觉得,难道cnt不是设置一下总的区间然后直接后续push_down就行了么,但是这样不行,我们每次只对相同覆盖次数的边进行覆盖次数和长度的更新,否则会出现一下提前返回的情况,或者说查询不方便的情况。所以在push_up的时候就会把最近的那个不一样覆盖次数的根节点打上-1的标记。由于所有区间的正常覆盖次数都是大于等于0的,所以-1是可以拿来做标记的。
代码如下所示:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<cmath>
#include<climits>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int MAXN=2222;
struct edge{
double l,r;
double h;
int f;
edge(){}
edge(double l,double r,double h,int f):l(l),r(r),h(h),f(f){}
bool operator<(const edge& e)const{
return this->h<e.h;
}
}g[MAXN];
int cnt[MAXN<<2];// 记录是否有边覆盖了这一段区域算作面积 -1表示当前不需要传递cnt
double sum[MAXN<<2];// 记录当前面积长度
double x[MAXN<<1];
// 按照cnt更新sum 同时将cnt的标记传递
// -1表示不用传递
void push_down(int l,int r,int rt){
int mid=(l+r)>>1;
if (cnt[rt]!=-1){
// cout<<"rt:"<<rt<<", cnt:"<<cnt[rt]<<endl;
cnt[rt<<1]+=cnt[rt];
cnt[rt<<1|1]+=cnt[rt];
sum[rt<<1]=(cnt[rt<<1] ? (x[mid+1]-x[l]) : 0);
sum[rt<<1|1]=(cnt[rt<<1|1] ? (x[r+1]-x[mid+1]) : 0);
cnt[rt]=0;
}
}
// 更新sum
void push_up(int rt){
if (cnt[rt<<1]==-1 || cnt[rt<<1|1]==-1){
cnt[rt]=-1;
} else if (cnt[rt<<1]!=cnt[rt<<1|1]){
cnt[rt]=-1;
} else{
cnt[rt]=cnt[rt<<1];
}
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
if (l==r){
cnt[rt]=0;
sum[rt]=0;
return;
}
int mid=(l+r)>>1;
build(lson);
build(rson);
push_up(rt);
}
void update(int a,int b,int val,int l,int r,int rt){
// cout<<"update a,b,l,r:"<<a<<","<<b<<","<<l<<","<<r<<endl;
if (a<=l && r<=b){
if (cnt[rt]!=-1){
cnt[rt]+=val;
sum[rt]=cnt[rt] ? (x[r+1]-x[l]) : 0;
// push_down(l,r,rt);
// push_up(rt);
return;
}
}
// cout<<"update rt:"<<rt<<","<<cnt[rt]<<endl;
push_down(l,r,rt);
int mid=(l+r)>>1;
if (a<=mid){
update(a,b,val,lson);
}
if (b>mid){
update(a,b,val,rson);
}
push_up(rt);
}
int main(){
int num;
int kase=0;
double x1,x2,y1,y2;
while (~scanf("%d",&num) && num){
int n=0,m=0;
for (int i = 0; i < num; ++i) {
scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
x[++n]=x1;
g[++m]=edge(x1,x2,y1,1);
x[++n]=x2;
g[++m]=edge(x1,x2,y2,-1);
}
sort(x+1,x+1+n);
sort(g+1,g+1+m);
n=unique(x+1,x+1+n)-x-1;
build(1,n-1,1);
double ans=0;
for (int i = 1; i < m; ++i) {
int l= upper_bound(x+1,x+1+n,g[i].l)-x-1;
int r= upper_bound(x+1,x+1+n,g[i].r)-x-2;
// cout<<"f:"<<g[i].f<<endl;
update(l,r,g[i].f,1,n-1,1);
// cout<<"l,r:"<<l<<","<<r<<endl;
// cout<<sum[1]<<","<<g[i].h<<","<<g[i+1].h<<endl;
// cout<<sum[2]<<","<<sum[3]<<","<<sum[4]<<","<<sum[5]<<endl;
// cout<<cnt[2]<<","<<cnt[3]<<","<<cnt[4]<<","<<cnt[5]<<endl;
ans+=sum[1]*(g[i+1].h-g[i].h);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",++kase,ans);
}
return 0;
}