今天来谈一谈线段树扫描线求相关的二维平面上多个矩形的面积之交、面积之并、周长等相关问题。
首先,有几道模板题目:
http://poj.org/problem?id=1177 [求周长]
http://acm.hdu.edu.cn/showproblem.php?pid=1255 [求面积之交]
http://acm.hdu.edu.cn/showproblem.php?pid=1542 [求面积之并]
这几道是扫描线的入门题目,也是裸的模板题目。(看题解时最好一边在纸画一下)
我的做法是先存上、下底边(线段),上底边标记是-1,下底边标记是1,枚举每一条线段“扫描线从下到上扫描”,将线段离散化成区间问题,用线段树存起来,变成询问区间求和(线段总和)问题。
如果是枚举到标记是1的边,那么在Update过程中要不断在相应的区间+1;相反如果是-1,那么久就-1,对枚举的每一条边都要Update一次。
其实三道题的模板都是一样的,主要在修改一下PushUp函数,因为原理是差不多的。
求周长:获得当前枚举线段后的总区间总长度len,同时要得到平行于y轴的线段条数,相应的乘上系数同时要加上“底边”长度(len -上一条边枚举后获得的“底边”长度),然后就没有然后了。
求面积之并:在Update后获得“底边”长度len后,乘上上一条边和这条边的距离就是当前枚举获得的面积。具体操作在PushUp修改。
求面积之交: 同样是在操作获得“底边”长度,只是在标记过程中要多加个判断,也就是覆盖次数大于1的才是我们想要的,得到之后的操作跟求面积并一样了。下面我分享一下自己“漂亮”的代码。【师兄说:写代码要跟写诗一样^_^】
求周长:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 10000+10;
int x[N];
struct Line{
int l,r,h,f;
}line[N];
struct Tree{
int len,num,cover;
bool lf,rf;
}tree[N<<2];
bool cmp(Line A,Line B){
return A.h<B.h;
}
void PushUp(int root,int l,int r){
if(tree[root].cover>0){
tree[root] = Tree{x[r+1]-x[l],1,tree[root].cover,1,1};
return ;
}
if(l==r){
tree[root] = Tree{0,0,0,0,0,};
return;
}
tree[root].lf = tree[root<<1].lf;
tree[root].rf = tree[root<<1|1].rf;
tree[root].len = tree[root<<1].len+tree[root<<1|1].len;
tree[root].num = tree[root<<1].num+tree[root<<1|1].num - (tree[root<<1].rf & tree[root<<1|1].lf);
}
void Update(int root,int l,int r,int ql,int qr,int f){
if(ql<=l && r<=qr){
tree[root].cover += f;
PushUp(root,l,r);
return ;
}
int mid = (l+r)>>1;
if(ql<=mid) Update(root<<1,l,mid,ql,qr,f);
if(mid<qr) Update(root<<1|1,mid+1,r,ql,qr,f);
PushUp(root,l,r);
}
int main(void){
// freopen("c.txt","r",stdin);
int n,x1,x2,y1,y2;
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
line[i] = Line{x1,x2,y1,1};
line[i+n] = Line{x1,x2,y2,-1};
x[i] = x1,x[i+n] = x2;
}
n <<= 1;
sort(x+1,x+1+n);
int tot = unique(x+1,x+1+n)-x-1;
sort(line+1,line+n+1,cmp);
int ans = 0;
int pre_num=0,pre_len=0;
for(int i=1;i<=n;i++){
int ql = lower_bound(x+1,x+1+tot,line[i].l)-x;
int qr = lower_bound(x+1,x+1+tot,line[i].r)-x-1;
Update(1,1,tot,ql,qr,line[i].f);
if(i>=2){
ans += pre_num * 2 * (line[i].h - line[i-1].h);
}
ans += abs(tree[1].len - pre_len);
pre_num = tree[1].num;
pre_len = tree[1].len;
}
printf("%d\n",ans);
}
return 0;
}
求面积之并:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 250;
double sum[N<<2],len[N<<2],x[N<<1];
struct Node{
double l,r,h;
int f;
}node[N<<1];
bool cmp(Node A,Node B){
return A.h<B.h;
}
void PushUp(int root,int l,int r){
if(len[root]) sum[root] = x[r+1] - x[l];
else if(l==r) sum[root] = 0;
else sum[root] = sum[root<<1] + sum[root<<1|1];
}
void Update(int root,int l,int r,int ql,int qr,int f){
if(ql<=l && r<=qr){
len[root] += f;
PushUp(root,l,r);
return ;
}
int mid = (l+r)>>1;
if(ql<=mid) Update(root<<1,l,mid,ql,qr,f);
if(mid<qr) Update(root<<1 | 1,mid+1,r,ql,qr,f);
PushUp(root,l,r);
}
int main(void){
int n,cas = 1;
//freopen("c.txt","r",stdin);
double x1,x2,y1,y2;
while(~scanf("%d",&n) && n){
memset(sum,0,sizeof(sum));
memset(len,0,sizeof(len));
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
node[i] = Node{x1,x2,y1,1};
node[i+n] = Node{x1,x2,y2,-1};
x[i] = x1,x[i+n] = x2;
}
n<<=1;
sort(x+1,x+n+1);
int tot = unique(x+1,x+n+1)-x-1;
sort(node+1,node+1+n,cmp);
double ans = 0;
for(int i=1;i<=n;i++){
int ql = lower_bound(x+1,x+tot+1,node[i].l)-x;
int qr = lower_bound(x+1,x+tot+1,node[i].r)-x-1;
Update(1,1,tot,ql,qr,node[i].f);
ans += sum[1]*(node[i+1].h-node[i].h);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n",cas++,ans);
}
return 0;
}
求面积之交:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 2000+10;
int cover[N<<2];
double len[N<<2],once[N<<2],x[N];
struct Node{
double l,r,h;
int f;
}node[N];
bool cmp(Node A,Node B){
if(A.h==B.h) return A.f>B.f;
return A.h<B.h;
}
void PushUp(int root,int l,int r){
if(cover[root]>=2){
once[root] = 0;
len[root] = x[r+1]-x[l];
}
else if(cover[root]==1){
if(l==r) len[root] = 0;
else{
len[root] = len[root<<1] + len[root<<1|1] + once[root<<1] + once[root<<1|1];
}
once[root] = x[r+1]-x[l]-len[root];
}
else if(cover[root]==0){
if(l==r) len[root] = once[root] = 0;
else{
len[root] = len[root<<1] + len[root<<1|1];
once[root] = once[root<<1] + once[root<<1|1];
}
}
}
void Update(int root,int l,int r,int ql,int qr,int f){
if(ql<=l && r<=qr){
cover[root] += f;
PushUp(root,l,r);
return ;
}
int mid = (l+r)>>1;
if(ql<=mid) Update(root<<1,l,mid,ql,qr,f);
if(mid<qr) Update(root<<1|1,mid+1,r,ql,qr,f);
PushUp(root,l,r);
}
int main(void){
int T;
// freopen("c.txt","r",stdin);
double x1,x2,y1,y2;
scanf("%d",&T);
while(T--){
int n;
memset(len,0,sizeof(len));
memset(once,0,sizeof(once));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
node[i] = Node{x1,x2,y1,1};
node[i+n] = Node{x1,x2,y2,-1};
x[i] = x1,x[i+n]=x2;
}
n <<= 1;
sort(x+1,x+n+1);
int tot = unique(x+1,x+n+1)-x-1;
sort(node+1,node+n+1,cmp);
double ans = 0;
for(int i=1;i<=n;i++){
int ql = lower_bound(x+1,x+1+tot,node[i].l)-x;
int qr = lower_bound(x+1,x+1+tot,node[i].r)-x-1;
Update(1,1,tot,ql,qr,node[i].f);
ans += len[1]*(node[i+1].h-node[i].h);
}
printf("%.2lf\n",ans);
}
return 0;
}