扫描线 模板小记

\[扫描线(Scanline)模板小记 \]


\(Algorithm:Scanline\)

扫描线算法 可以计算几何中图形并交问题
\(Problem\) \(link\)
算法本质上是线段树

\(Tips:\)

  1. 横坐标需记录 注意分别是\((i\times 2-1)\)\((i\times 2)\) 一条线段有两个端点 位运算要打括号
  2. 为了计算横边的长度 将矩形的上边赋为\(-1\) 下边赋为\(1\)
  3. \(y\)坐标升序排序 这样就能保证每次扫描线先扫到下边 保证线段的长度非负
  4. \(x\)坐标离散化 可用\(stl\)\(unique\)
  5. 线段树维护 该线段被覆盖了多少次 该线段内被图形截的长度
  6. 数组都要开大一些 \(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;
}
posted @ 2022-01-19 21:37  EschatonRin  阅读(8)  评论(0)    收藏  举报