扫描线第一题,求矩形面积的并。思路很好理解,但是实现上有很多的问题。
1 维护的变量ala[o]代表含义是“当前节点对应区间全覆盖的点数”,举个例子来说,现在的区间各个节点的值如果是2 3 3 3 5 5 2,那么ala就应该是2。这个变量这么做的好处就是,当ala不为0的时候一定可以直接把整个区间的Len算出来:就是x[r]-x[l],当他为0的时候说明中间有的点为0,那么我们再考虑左右儿子Len之和。这个东西注意是不要pushdown的,如果pushdown了反而会使得问题变得复杂,因为如果你只是往里面+1也要pushdown的话,那等于原来的可以直接得到的Len现在反而要往下走才可以得到。那么不pushdown会不会出现重复加的情况?也不会,因为这个问题里所有的形状都是矩形,有一个上下边使得ala[o]+1,则一定有另外一个边使得他-1,而这中间的呢?因为肯定这一条线段都是还是属于计算范围的,所以也没有多加,一定是对的,这个特点很重要,今早赖床的时候想了下才明白其中的奥妙所在。。。。
引用一位dalao的话描述一下
这里的区间覆盖不需要打懒惰标记。为什么呢?因为很显然,每个下边都有与之对应的上边,它显然不会被比它的长度小的上边给清除。而如果有比它长的上边,也一定有和那个上边对应的下边还存在。所以用那个上边清除下边的时候,一定会先递归到那个更长的下边,还没有递归到它就停止递归了。所以每个上下边是一一对应的,无需下传标记等。
2 这个线段树不是[l,r],而是[l,r),因为我们这里离散化之后,如果按照闭区间来算,那[mid,mid+1]这一段就不能被计算到了,所以要用左闭右开,这样的话对应的一开始的区间右边要+1,而这样又会有问题就是原来的整个线段,也就是o==1时候的线段,以及所有的右侧端点为整个线段树右端点的线段,他们的右侧怎么处理,这可是个必须要左闭右闭的啊?我这里用的是在xs离散化数组的后面加上一个和xs[cnt1]相等的元素,这样就可以不用特殊判断了。
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long LL;
const int maxn =132674*2+10;
struct seg
{
double x1,x2,y;
int flag;
bool operator<(const struct seg &rhs)const {return y<rhs.y;}
} segs[205];
// discretize
int cnt1;
double xs[205];
int Hash(double m)
{
return lower_bound(xs+1,xs+1+cnt1,m)-xs;
}
double Len[maxn<<2];
int ala[maxn<<2];
void maintain(int o,int l,int r)
{
if(l==r)//empty segment
Len[o]=0.0;
else if(ala[o]>0)
Len[o]=(xs[r]-xs[l]);
else
Len[o]=Len[o<<1]+Len[o<<1|1];
}
int pl,pr,v;
void update(int o,int l,int r)//[l,r)
{
if(l>r-1)//[l,r)
return ;
if(pl<=l&&r<pr)
{
ala[o]+=v;
}
else if(l!=r-1)//not leaf node
{
//pushdown(o,l,r);//do not pushdown for the property of the problem
int mid=(l+r)>>1;
if(pl<mid)
update(o<<1,l,mid);
if(mid<pr)
update(o<<1|1,mid,r);
}
maintain(o,l,r);
}
int main()
{
int T,cs=0,n;
double x1,y1,x2,y2,ans,h;
while(~scanf("%d",&n)&&n)
{
memset(ala,0,sizeof(ala));
memset(Len,0,sizeof(Len));//error1
ans=0.0;
cs++;
cnt1=0;
for(int i=1; i<=n; i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
xs[++cnt1]=x1,xs[++cnt1]=x2;
segs[2*i-1].x1=x1,segs[2*i-1].x2=x2,segs[2*i-1].y=y1,segs[2*i-1].flag=1;
segs[2*i].x1=x1,segs[2*i].x2=x2,segs[2*i].y=y2,segs[2*i].flag=-1;
}
sort(segs+1,segs+1+2*n);
sort (xs+1,xs+1+cnt1);
cnt1=1;
for (int i=2; i<=2*n; i++)
if (xs[i]!=xs[cnt1])
xs[++cnt1]=xs[i];
xs[cnt1+1]=xs[cnt1];//[l,r)
for (int i=1;i<((n<<1));i++)
{
h=segs[i+1].y-segs[i].y;
v=segs[i].flag;
pl=Hash(segs[i].x1),pr=Hash(segs[i].x2)+1;
update(1,1,cnt1+1);
ans+=Len[1]*h;
}
printf("Test case #%d\nTotal explored area: %.2f\n\n",cs,ans);
}
return 0;
}
