扫描线 模板小记
\[扫描线(Scanline)模板小记
\]
\(Algorithm:Scanline\)
扫描线算法 可以计算几何中图形并交问题
\(Problem\) \(link\)
算法本质上是线段树
\(Tips:\)
- 横坐标需记录 注意分别是\((i\times 2-1)\)和\((i\times 2)\) 一条线段有两个端点 位运算要打括号
- 为了计算横边的长度 将矩形的上边赋为\(-1\) 下边赋为\(1\)
- 将\(y\)坐标升序排序 这样就能保证每次扫描线先扫到下边 保证线段的长度非负
- 将\(x\)坐标离散化 可用\(stl\)的\(unique\)
- 线段树维护 该线段被覆盖了多少次 该线段内被图形截的长度
- 数组都要开大一些 \(10\)倍较稳妥 再根据具体需求开大
对于本题 面积就是长\((len)\times\) 宽\((heigh)\)
\(CODE:\)
点击查看代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,id[N<<1];
ll ans;
struct Scanline{
ll l,r,heigh,val;
}line[N<<1];
struct SegmentTree{
int l,r,cnt;
ll len;
}a[N<<2];
bool cmp(Scanline x,Scanline y){return x.heigh<y.heigh;}
void build(int x,int l,int r)
{
a[x]=(SegmentTree){l,r,0,0};
if(l==r) return;
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
return;
}
void up(int x)
{
int l=a[x].l,r=a[x].r;
if(a[x].cnt) a[x].len=id[r+1]-id[l];
else a[x].len=a[x<<1].len+a[x<<1|1].len;
}
void ins(int x,ll L,ll R,ll k)
{
int l=a[x].l,r=a[x].r;
if(id[r+1]<=L||R<=id[l]) return;
if(L<=id[l]&&id[r+1]<=R)
{
a[x].cnt+=k;
up(x);
return;
}
ins(x<<1,L,R,k);
ins(x<<1|1,L,R,k);
up(x);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
ll x1,y1,x2,y2;
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
id[(i<<1)-1]=x1;id[i<<1]=x2;
line[(i<<1)-1]=(Scanline){x1,x2,y1,1};
line[i<<1]=(Scanline){x1,x2,y2,-1};
}
n<<=1;
sort(line+1,line+n+1,cmp);
sort(id+1,id+n+1);
int tot=unique(id+1,id+n+1)-id-1;
build(1,1,tot-1);
for(int i=1;i<n;i++)
{
ins(1,line[i].l,line[i].r,line[i].val);
ans+=a[1].len*(line[i+1].heigh-line[i].heigh);
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号