总结一下关于扫描线的习题。正在更新中...
Table of Contents
HDU-1542-Atlantis,POJ-1151-Atlantis(扫描线+线段树)
HDU-1828-Picture||POJ-1177 Picture(矩形的周长,扫描线)
HDU-1542-Atlantis,POJ-1151-Atlantis(扫描线+线段树)
这是一道题,同样的代码都能AC。
题目链接:http://poj.org/problem?id=1151
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542
题目大意:给你n个矩形,求出它们总共的覆盖面积。
思路:下面就是板子。
ACCode:
// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
// register
const int MAXN=3e2+10;
const int INF32=0x3f3f3f3f;
const LL INF64=0x3f3f3f3f3f3f3f3f;
const int MOD=1e9+7;
const double EPS=1.0e-12;
const double PI=acos(-1.0);
struct Line{
double x1,y1,y2;int flag;//falg记录该线段是应该添加还是消除
Line(double _x1=0,double _y1=0,double _y2=0,int _flag=0){
x1=_x1;y1=_y1;y2=_y2;flag=_flag;
}
friend int operator < (Line a,Line b){
return a.x1<b.x1;
}
};
struct Node{
double len;int l,r,flag;//flag记录该范围有没有被完全覆盖
Node(double _len=0,int _l=0,int _r=0,int _flag=0){
len=_len;l=_l;r=_r;flag=_flag;
}
};
Line line[MAXN<<2];
Node tree[MAXN<<3];
double y[MAXN<<2];
int n;
int Location_x(double key,int l,int r){
while(l<=r){
int mid=(l+r)>>1;
if(fabs(y[mid]-key)<EPS) return mid;
else if(y[mid]>key) r=mid-1;
else l=mid+1;
}return -1;
}
void Build(int l,int r,int rt){
tree[rt]=Node(0,l,r,0);
if(l+1==r) return ;
int mid=(l+r)>>1;
Build(l,mid,rt<<1);Build(mid,r,rt<<1|1);
}
void PushUp(int rt){
//这里必定存在flag>=0,因为想要减去,必须先加上
if(tree[rt].flag>0){
tree[rt].len=y[tree[rt].r]-y[tree[rt].l];
return ;
}
if(tree[rt].l+1==tree[rt].r) tree[rt].len=0;
else tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
}
void Update(int ql,int qr,int v,int rt){
if(tree[rt].l==ql&&tree[rt].r==qr){
tree[rt].flag+=v;
PushUp(rt);return ;
}
if(qr<=tree[rt<<1].r) Update(ql,qr,v,rt<<1);
else if(ql>=tree[rt<<1|1].l) Update(ql,qr,v,rt<<1|1);
else{
Update(ql,tree[rt<<1].r,v,rt<<1);
Update(tree[rt<<1|1].l,qr,v,rt<<1|1);
}PushUp(rt);
}
int main(){
int Case=1;
while(~scanf("%d",&n)){
if(n==0) break;
double x1,x2,y1,y2;
for(int i=1;i<=n;++i){
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[i]=Line(x1,y1,y2,1);//+1代表增加一条边 增加这条边所对应的的长度
line[i+n]=Line(x2,y1,y2,-1);//-1代表消去这条边 ,将这条边对线段树的影响消除
y[i]=y1;y[i+n]=y2;
}n*=2;
sort(y+1,y+1+n);sort(line+1,line+1+n);
int temp=2;//将离散的y轴进行去重
for(int i=2;i<=n;++i){
if(fabs(y[i]-y[i-1])>EPS) y[temp++]=y[i];
}--temp;
Build(1,temp,1);//初始化线段树
double ans=0;int l,r;
for(int i=1;i<n;++i){
//找到一个更新的范围
l=Location_x(line[i].y1,1,temp);r=Location_x(line[i].y2,1,temp);
Update(l,r,line[i].flag,1);//对线段树进行更新
ans+=tree[1].len*(line[i+1].x1-line[i].x1);
}printf("Test case #%d\nTotal explored area: %.2lf\n\n",Case++,ans);
}
}
/*
2
10 10 20 20
15 15 25 25.5
0
*/
从下向上的扫描以x轴建树。与上面的一样,我就不重复工作了。
另:有些dalao在建树和更新的时候使用对[l,mid]和[mid+1,r]区间进行更新,感觉似乎是对点进行更新。代码有着细微的区别。结果是一样的,目前我还没发现什么除了代码上的不同(个人觉得更新区间更好理解一点)。
简单的扫描线板子就到这里了,至于更多的扫描线应用,再说吧。
扩展一下,求两个以上矩形的覆盖的同一个面积的和。
HDU-1255-覆盖的面积(扫描线+线段树,板子)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1255
题目大意:中文题,很清楚
思路:上面有一个求所有覆盖面积的题,我们只用新加一个参数,表示覆盖的区间是两次重复的即可。要怎么表示呢?
经过简单的分析,我们得出可以有这几种情况:
1.如果这个区间没有被覆盖,不用去管他。
2.如果一个区间被覆盖了两次及以上,那么这个区间的有效长度就是整个区间的长度。
3.如果这个区间被覆盖了一次,那么可以确定的是,这个区间必定没有被全部覆盖,但是可能被部分覆盖。那么这个区间的有效长度就是所有子节点被覆盖1次的长度,再加上本身的这个长度,就是覆盖两次||以上的有效长度。
模拟一下就会发现3的作用。
只用在PushUp中改一下就好了。
ACCode:
// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
// register
const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const LL INF64=0x3f3f3f3f3f3f3f3f;
const int MOD=1e9+7;
const double EPS=1.0e-12;
const double PI=acos(-1.0);
struct Line{
double x1,y1,y2;int flag;//falg记录该线段是应该添加还是消除
Line(double _x1=0,double _y1=0,double _y2=0,int _flag=0){
x1=_x1;y1=_y1;y2=_y2;flag=_flag;
}
friend int operator < (Line a,Line b){
return a.x1<b.x1;
}
};
struct Node{
double len,len2;int l,r,flag;//flag记录该范围有没有被完全覆盖
Node(double _len=0,double _len2=0,int _l=0,int _r=0,int _flag=0){
len=_len;len2=_len2;l=_l;r=_r;flag=_flag;
}
};
Line line[MAXN<<2];
Node tree[MAXN<<3];
double y[MAXN<<2];
int n;
int Location_x(double key,int l,int r){
while(l<=r){
int mid=(l+r)>>1;
if(fabs(y[mid]-key)<EPS) return mid;
else if(y[mid]>key) r=mid-1;
else l=mid+1;
}return -1;
}
void Build(int l,int r,int rt){
tree[rt]=Node(0,0,l,r,0);
if(l+1==r) return ;
int mid=(l+r)>>1;
Build(l,mid,rt<<1);Build(mid,r,rt<<1|1);
}
void PushUp(int rt){
if(tree[rt].flag) tree[rt].len=y[tree[rt].r]-y[tree[rt].l];
else if(tree[rt].l+1==tree[rt].r) tree[rt].len=0;
else tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
if(tree[rt].flag>1) tree[rt].len2=y[tree[rt].r]-y[tree[rt].l];
else if(tree[rt].l+1==tree[rt].r) tree[rt].len2=0;//没有覆盖两次&&是一个单位
else if(tree[rt].flag==1) tree[rt].len2=tree[rt<<1].len+tree[rt<<1|1].len;
else tree[rt].len2=tree[rt<<1].len2+tree[rt<<1|1].len2;
}
//void PushDown(int rt){
// if(tree[rt].flag==1){
//
// }
//}
void Update(int ql,int qr,int v,int rt){
// PushDown(rt);
if(tree[rt].l==ql&&tree[rt].r==qr){
tree[rt].flag+=v;
PushUp(rt);return ;
}
if(qr<=tree[rt<<1].r) Update(ql,qr,v,rt<<1);
else if(ql>=tree[rt<<1|1].l) Update(ql,qr,v,rt<<1|1);
else{
Update(ql,tree[rt<<1].r,v,rt<<1);
Update(tree[rt<<1|1].l,qr,v,rt<<1|1);
}PushUp(rt);
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d",&n);
double x1,x2,y1,y2;
for(int i=1;i<=n;++i){
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[i]=Line(x1,y1,y2,1);//+1代表增加一条边 增加这条边所对应的的长度
line[i+n]=Line(x2,y1,y2,-1);//-1代表消去这条边 ,将这条边对线段树的影响消除
y[i]=y1;y[i+n]=y2;
}n*=2;
sort(y+1,y+1+n);sort(line+1,line+1+n);
int temp=2;//将离散的y轴进行去重
for(int i=2;i<=n;++i){
if(fabs(y[i]-y[i-1])>EPS) y[temp++]=y[i];
}--temp;
Build(1,temp,1);//初始化线段树
double ans=0;int l,r;
for(int i=1;i<n;++i){
//找到一个更新的范围
l=Location_x(line[i].y1,1,temp);r=Location_x(line[i].y2,1,temp);
Update(l,r,line[i].flag,1);//对线段树进行更新
// cout<<"Update l,r,y[l],y[r],len2 :"<<l<<" "<<r<<" "<<y[l]<<" "<<y[r]<<" "<<tree[1].len2<<endl;
ans+=tree[1].len2*(line[i+1].x1-line[i].x1);
}printf("%.2lf\n",ans);
}
}
/*
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
*/
HDU-1828-Picture||POJ-1177 Picture(矩形的周长,扫描线)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1828
题目链接:http://poj.org/problem?id=1177
题目大意:n个矩形放在一起,求覆盖的周长。
思路:扫描线,扫描一遍加和得出一条边的长度,同时维护两边的边长。注意节点表示的区间合并的时候,要注意左右两个线重合的部分要去掉(即pl,pr部分);
ACCode:
// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
// register
const int MAXN=1e4+10;
const int INF32=0x3f3f3f3f;
const LL INF64=0x3f3f3f3f3f3f3f3f;
const int MOD=1e9+7;
const double EPS=1.0e-8;
const double PI=acos(-1.0);
struct Line{
double x1,y1,y2;int flag;
Line(double _x1=0,double _y1=0,double _y2=0,int _flag=0){
x1=_x1;y1=_y1;y2=_y2;flag=_flag;
}
friend int operator < (Line a,Line b){
return a.x1<b.x1;
}
};
struct Node{
double len;int l,r,flag,num,lp,rp;
Node(double _len=0,int _l=0,int _r=0,int _flag=0,int _num=0,int _lp=0,int _rp=0){
len=_len;l=_l;r=_r;flag=_flag;num=_num;lp=_lp;rp=_rp;
}
};
Line line[MAXN<<2];
Node tree[MAXN<<3];
double y[MAXN<<2];
int n;
int Location_x(double key,int l,int r){
while(l<=r){
int mid=(l+r)>>1;
if(fabs(y[mid]-key)<EPS) return mid;
else if(y[mid]>key) r=mid-1;
else l=mid+1;
}return -1;
}
void Build(int l,int r,int rt){
tree[rt]=Node(0,l,r,0,0,0,0);
if(l+1==r) return ;
int mid=(l+r)>>1;
Build(l,mid,rt<<1);Build(mid,r,rt<<1|1);
}
void PushUp(int rt){
if(tree[rt].flag){//被完全覆盖
tree[rt].len=y[tree[rt].r]-y[tree[rt].l];
tree[rt].lp=tree[rt].rp=1;
tree[rt].num=1;
}
else if(tree[rt].l+1==tree[rt].r){
tree[rt].len=0;
tree[rt].lp=tree[rt].rp=0;
tree[rt].num=0;
}
else{
tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
tree[rt].lp=tree[rt<<1].lp;tree[rt].rp=tree[rt<<1|1].rp;
tree[rt].num=tree[rt<<1].num+tree[rt<<1|1].num-(tree[rt<<1].rp&tree[rt<<1|1].lp);
}
}
void Update(int ql,int qr,int v,int rt){
// cout<<"rt,ql,qr :"<<rt<<" "<<ql<<" "<<qr<<endl;
if(ql==tree[rt].l&&qr==tree[rt].r){
tree[rt].flag+=v;
PushUp(rt);return ;
}
if(qr<=tree[rt<<1].r) Update(ql,qr,v,rt<<1);
else if(ql>=tree[rt<<1|1].l) Update(ql,qr,v,rt<<1|1);
else{
// cout<<rt<<" "<<(rt<<1)<<" "<<(rt<<1|1)<<" "<<tree[rt<<1].r<<" "<<tree[rt<<1|1].l<<endl;
Update(ql,tree[rt<<1].r,v,rt<<1);
Update(tree[rt<<1|1].l,qr,v,rt<<1|1);
}PushUp(rt);
}
int main(){
while(~scanf("%d",&n)){
double x1,x2,y1,y2;
for(int i=1;i<=n;++i){
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[i]=Line(x1,y1,y2,1);line[i+n]=Line(x2,y1,y2,-1);
y[i]=y1;y[i+n]=y2;
}n*=2;sort(y+1,y+1+n);sort(line+1,line+1+n);
int temp=2;
for(int i=2;i<=n;++i){
if(fabs(y[i]-y[i-1])>EPS) y[temp++]=y[i];
}--temp;
// for(int i=1;i<=n;++i){
// cout<<line[i].x1<<" "<<line[i].y1<<" "<<line[i].y2<<" "<<line[i].flag<<endl;
// }
// for(int i=1;i<=temp;++i){
// cout<<y[i]<<" ";
// }cout<<endl;
Build(1,temp,1);
// for(int i=1;i<=(n<<1);++i){
// cout<<"rt,l,r :"<<i<<" "<<tree[i].l<<" "<<tree[i].r<<endl;
// }
double ans=0,prelen=0;
line[n+1]=line[n];
for(int i=1;i<=n;++i){
int l=Location_x(line[i].y1,1,temp),r=Location_x(line[i].y2,1,temp);
Update(l,r,line[i].flag,1);
// cout<<tree[1].len<<endl;
ans+=fabs(tree[1].len-prelen);prelen=tree[1].len;
ans+=(line[i+1].x1-line[i].x1)*(2*tree[1].num);
}printf("%.0lf\n",ans);
}
}
/*
*/