题目链接:哆啦A梦传送门
题意:给出n个矩形围城的多边形,求它们的总周长。
题解:
跟hdu 1542解法差不多,也是扫描线求。
对于扫描线从下往上扫,每次横轴的长度为=|上一次的有效长度-这次的有效长度|。
对于竖轴的长度,我们在每个节点添加lc,rc,num,分别表示该节点的左右端点是否被覆盖(lc,rc为1表示被覆盖),num表示此节点代表的区间里有多少条线段。
那么对于完全覆盖的节点,它们的lc,rc,num都为1。
对于单节点,它们的lc,rc,num都为0。
对于不完全覆盖的节点,他的lc等于该节点的左子节点的lc,rc等于该节点的右子节点的rc,num等于左右子节点的num相加再减去两区间合并时是否对覆盖产生的影响(左子节点的rc&右子节点的lc(为1说明有重叠))。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=10000;
struct node{
double l,r,h; ///每条线的左右x端点,该线的纵坐标
int d; ///d=1为下边,d=-1为上边
}a[N];
bool cmp(node X,node Y)
{
return X.h<Y.h;
}
///存储横坐标,离散化使用
double x[N];
struct Node{
int tl,tr;
int s;///该节点被是否被完全覆盖,s不为0即为完全覆盖
int len,num; ///该节点的有效长度,该节点里有多少条线段
int lc,rc; ///该节点的左右端点是否被覆盖
}tree[N<<2];
void build(int root,int l,int r)
{
tree[root].tl=l;
tree[root].tr=r;
tree[root].len=tree[root].s=0;
tree[root].num=tree[root].lc=tree[root].rc=0;
if(l==r) return ;
else{
int mid=(l+r)>>1;
build(root*2,l,mid);
build(root*2+1,mid+1,r);
}
}
void pushup(int root)
{
if(tree[root].s){ ///该节点被完全覆盖
tree[root].len=x[tree[root].tr]-x[tree[root].tl-1];
tree[root].lc=tree[root].rc=1;
tree[root].num=1;
}
else if(tree[root].tl==tree[root].tr){ ///这是一个点而不是一条线段
tree[root].len=0;
tree[root].lc=tree[root].rc=tree[root].num=0;
}
else {///是一条没有被完全覆盖的区间,合并左右子节点
tree[root].len=tree[root*2].len+tree[root*2+1].len;
tree[root].lc=tree[root*2].lc;
tree[root].rc=tree[root*2+1].rc;
tree[root].num=tree[root*2].num+tree[root*2+1].num-(tree[root*2].rc&tree[root*2+1].lc);
}
}
void update(int root,int ql,int qr,int val)
{
int tl=tree[root].tl,tr=tree[root].tr;
if(ql<=tl&&tr<=qr){ ///更新区间包含该节点区间,说明该节点区间被完全覆盖
tree[root].s+=val;
pushup(root);
return;
}
int mid=(tl+tr)>>1;
if(mid>=ql) update(root*2,ql,qr,val);
if(mid<qr) update(root*2+1,ql,qr,val);
pushup(root);
}
int main()
{
int n,T=0;
while(~scanf("%d",&n))
{
int m=0;
for(int i=1;i<=n;i++)
{
double x1,x2,y1,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
a[m].l=x1,a[m].r=x2,a[m].h=y1,a[m].d=1;
x[m++]=x1;
a[m].l=x1,a[m].r=x2,a[m].h=y2,a[m].d=-1;
x[m++]=x2;
}
sort(x,x+m);
sort(a,a+m,cmp);
int len=unique(x,x+m)-x;
// printf("len=%d\n",len);
// for(int i=0;i<=len;i++)
// printf("%d=%f ",i,x[i]);
// puts("");
build(1,1,len);
double sum=0;
double last=0;
for(int i=0;i<m;i++) ///离散化各点,并更新线段树
{
///因为我们的树节点是要记录线段的长度,而不是点的权值
///这里我们可以将a[i].l的位置+1,这样在更新的时候我们就可以a[r]-a[l-1]来计算长度
///这里其实每个叶子节点都代表相邻x的一小段长度
int l=lower_bound(x,x+len,a[i].l)-x+1;
int r=lower_bound(x,x+len,a[i].r)-x;
update(1,l,r,a[i].d);
///tree[1].len表示整个区间的有效长度
// sum+=(a[i+1].h-a[i].h)*tree[1].len;
sum+=fabs(tree[1].len-last);///求上下边长
sum+=(a[i+1].h-a[i].h)*tree[1].num*2; ///求左右边长
last=tree[1].len;
}
printf("%.0f\n",sum);
}
return 0;
}