传送门
题目大意:给出n个矩形求交的面积。
扫面线的裸题
将纵坐标离散,并且将矩形分解成左右两个边界,按照横坐标排序
线段树的每一个节点[l,r]存储的是dis(l-1~r)这一段离散化之前的距离,维护一个标记表示区间被完全覆盖了几次,每一次修改update就行了
由于扫描线问题的特殊性,不存在在一个大区间里抠去一个小区间的问题,所以这个做法还是非常科学的
#include
#include
#include
#include
#include
using namespace std;
#define N 1005
int T,n,cnt,LSH;double ans;
struct SQU{double a,b,c,d;}squ[N];
struct LINE{double x;int l,r,val;}line[N];
double lsh[N],sum[N*4],len[N*4];int cover[N*4];
void clear()
{
cnt=LSH=0;ans=0.0;
memset(lsh,0,sizeof(lsh));memset(sum,0,sizeof(sum));memset(len,0,sizeof(len));memset(cover,0,sizeof(cover));
}
int cmp(LINE a,LINE b)
{
return a.xvoid update(int now,int l,int r)
{
if (cover[now]) sum[now]=len[now];
else if (l==r) sum[now]=0;
else sum[now]=sum[now<<1]+sum[now<<1|1];
}
void build(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
len[now]=lsh[l]-lsh[l-1];
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
len[now]=len[now<<1]+len[now<<1|1];
}
void change(int now,int l,int r,int lr,int rr,int v)
{
if (lr>rr) return;
int mid=(l+r)>>1;
if (lr<=l&&r<=rr)
{
cover[now]+=v;update(now,l,r);
return;
}
if (lr<=mid) change(now<<1,l,mid,lr,rr,v);
if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,v);
update(now,l,r);
}
int main()
{
while (~scanf("%d",&n))
{
if (!n) break;
clear();
for (int i=1;i<=n;++i)
{
scanf("%lf%lf%lf%lf",&squ[i].a,&squ[i].b,&squ[i].c,&squ[i].d);
lsh[++LSH]=squ[i].a,lsh[++LSH]=squ[i].c;
}
sort(lsh+1,lsh+LSH+1);LSH=unique(lsh+1,lsh+LSH+1)-lsh-1;
for (int i=1;i<=n;++i)
{
int a,c;double b=squ[i].b,d=squ[i].d;
a=lower_bound(lsh+1,lsh+LSH+1,squ[i].a)-lsh;
c=lower_bound(lsh+1,lsh+LSH+1,squ[i].c)-lsh;
line[++cnt].x=b,line[cnt].l=a,line[cnt].r=c,line[cnt].val=1;
line[++cnt].x=d,line[cnt].l=a,line[cnt].r=c,line[cnt].val=-1;
}
sort(line+1,line+cnt+1,cmp);
build(1,1,LSH);
for (int i=1,j;i<=cnt;i=j)
{
j=i;
if (i>1) ans+=sum[1]*(line[i].x-line[i-1].x);
while (line[i].x==line[j].x)
{
change(1,1,LSH,line[j].l+1,line[j].r,line[j].val);
++j;
}
}
printf("Test case #%d\n",++T);
printf("Total explored area: %.2lf\n",ans);
puts("");
}
}