【学习笔记】扫描线

一.前言

早就学了扫描线了,但是有一道题当时没做,现在才做,于是就来写写学习笔记。
哎我学习笔记前面咋老是这么多废话啊

二.定义

扫描线其实是一种思想,就是遍历某个值并将其加入数据结构,同时动态地解决一些问题。
听起来很抽象,那就看例题吧。

三.例题

[poj1151]亚特兰蒂斯

求矩形面积并。想象一条线从下往上扫整个平面,就能求出在每个矩形的上下边界处横向覆盖的距离。画出来长这样:

我想这也是这个思想叫扫描线的原因。
因此我们从下往上插入矩形的上下边,在下面加入,在上面删除,顺带统计答案就好了。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ld long double
#define x1(i) a[i].x1
#define x2(i) a[i].x2
#define y(i) a[i].y
#define tag(i) a[i].tag
#define lid id<<1
#define rid id<<1|1
#define l(id) tr[id].l
#define r(id) tr[id].r
#define cnt(id) tr[id].cnt
#define len(id) tr[id].len
#define lb lower_bound
using namespace std;
const int maxn=1e4+5;
int n,m;
struct line{
	ld x1,x2,y;
	int tag;
	il bool operator<(const line &x){return y<x.y;}
}a[maxn<<1];
ld b[maxn<<1],ans;
struct seg{
	int l,r,cnt;
	ld len;
}tr[maxn<<3];
il void build(int id,int l,int r){
	tr[id]=(seg){l,r,0,0};
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(lid,l,mid),build(rid,mid+1,r);
}
il void pushup(int id){
	int l=l(id),r=r(id);
	if(cnt(id)) len(id)=b[r+1]-b[l];
	else len(id)=len(lid)+len(rid);
}
il void upd(int id,int l,int r,int tag){
	if(l(id)>=l and r(id)<=r){
		cnt(id)+=tag;
		pushup(id);
		return ;
	}
	int mid=(l(id)+r(id))>>1;
	if(l<=mid) upd(lid,l,r,tag);
	if(r>mid) upd(rid,l,r,tag);
	pushup(id);
}
int main(){
	for(int T=1;;T++){
		scanf("%d",&n);
		if(!n) break;
		for(int i=1;i<=n;i++){
			scanf("%Lf%Lf%Lf%Lf",&x1(i),&y(i),&x2(i),&y(i+n));
			x1(i+n)=x1(i),x2(i+n)=x2(i);
			tag(i)=1,tag(i+n)=-1;
			b[i]=x1(i),b[i+n]=x2(i);
		}
		n<<=1;
		sort(a+1,a+n+1),sort(b+1,b+n+1);
		m=unique(b+1,b+n+1)-b-1;
		ans=0,build(1,1,m-1);
		for(int i=1,l,r;i<n;i++){
			l=lb(b+1,b+m+1,x1(i))-b;
			r=lb(b+1,b+m+1,x2(i))-b;
			upd(1,l,r-1,tag(i));
			ans+=len(1)*(y(i+1)-y(i));
		}
		printf("Test case #%d\nTotal explored area: %.2Lf\n",T,ans);
	}
	return 0;
}

[IOI1998] [USACO5.5] 矩形周长Picture

求周长,显然直接扫描线就好了。横着跑一遍再竖着跑一遍。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define lid tr[id].ls
#define rid tr[id].rs
#define len(id) tr[id].len
#define cnt(id) tr[id].cnt
#define lb lower_bound
using namespace std;
namespace cplx{bool begin;}
const int maxn=5e3+5;
int n,lsh[maxn<<1],tot,num,rt,ans;
int xa[maxn],ya[maxn],xb[maxn],yb[maxn];
struct xian{
	int x1,x2,y,tag;
	il bool operator<(const xian &x){return y<x.y;}
}a[maxn<<1];
struct seg{int ls,rs,len,cnt;}tr[maxn<<2];
il void build(int &id,int l,int r){
//	cout<<l<<" "<<r<<"\n";
	id=++num;
	len(id)=cnt(id)=lid=rid=0;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
}
il void pushup(int id,int l,int r){
	if(cnt(id)) len(id)=lsh[r+1]-lsh[l];
	else len(id)=len(lid)+len(rid);
}
il void upd(int id,int L,int R,int l,int r,int val){
	if(L>=l and R<=r)
		return (void)(cnt(id)+=val,pushup(id,L,R));
	int mid=(L+R)>>1;
	if(l<=mid) upd(lid,L,mid,l,r,val);
	if(r>mid) upd(rid,mid+1,R,l,r,val);
	pushup(id,L,R);
//	cout<<L<<" "<<R<<" "<<cnt(id)<<"\n";
}
il void solve(){
	tot=num=rt=0;
	for(int i=1;i<=n;i++)
		lsh[++tot]=a[i].x1,lsh[++tot]=a[i].x2;
	sort(lsh+1,lsh+tot+1);
	tot=unique(lsh+1,lsh+tot+1)-lsh-1;
//	cout<<tot<<"\n";
//	for(int i=1;i<=tot;i++) cout<<lsh[i]<<" ";
//	puts("");
	n<<=1;
	sort(a+1,a+n+1);
	build(rt,1,tot-1);
//	for(int id=1;id<=num;id++)
//		cout<<id<<" "<<lid<<" "<<rid<<" "<<len(id)<<" "<<cnt(id)<<"\n";
//	puts("");
	for(int i=1,lst=0,t1,t2;i<=n;i++){
		t1=lb(lsh+1,lsh+tot+1,a[i].x1)-lsh;
		t2=lb(lsh+1,lsh+tot+1,a[i].x2)-lsh;
//		cout<<t1<<" "<<t2<<"\n";
		upd(rt,1,tot-1,t1,t2-1,a[i].tag);
		ans+=abs(len(1)-lst);
		lst=len(1);
//		cout<<a[i].x1<<" "<<a[i].x2<<" "<<a[i].y<<" "<<a[i].tag<<" ";
//		puts("");
//		cout<<t1<<" "<<t2<<" "<<lst<<" "<<cnt(1)<<"\n";
	}
	n>>=1;
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n);
//	cout<<n<<"\n";
	for(int i=1;i<=n;i++){
		read(xa[i])read(ya[i]);
		read(xb[i])read(yb[i]);
//		cout<<xa[i]<<" "<<ya[i]<<" "<<xb[i]<<" "<<yb[i]<<"\n";
	}
	for(int i=1;i<=n;i++){
		a[i].x1=a[i+n].x1=xa[i];
		a[i].x2=a[i+n].x2=xb[i];
		a[i].y=ya[i],a[i+n].y=yb[i];
		a[i].tag=1,a[i+n].tag=-1;
	}
	solve();
//	cout<<n<<"\n";
	for(int i=1;i<=n;i++){
		a[i].x1=a[i+n].x1=ya[i];
		a[i].x2=a[i+n].x2=yb[i];
		a[i].y=xa[i],a[i+n].y=xb[i];
		a[i].tag=1,a[i+n].tag=-1;
	}
	solve();
	printf("%d",ans);
	return 0;
}

[poj2482]窗口的星星

考虑一个星星,能覆盖它的矩形的右上角是在一个区间 \([x,x+W-1]\) 中的。于是扫描线区间修改整体查询最大值即可。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pb push_back
#define lwrb lower_bound
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e4+5;
int n,W,H,lsh[maxn],cnt,rt;
int tr[maxn*70],tag[maxn*70];
int ls[maxn*70],rs[maxn*70];
struct node{
	int x,y,c;
}a[maxn];
vector<int> cun[maxn];
il void pushup(int id){
	tr[id]=max(tr[ls[id]],tr[rs[id]]);
}
il void pushtag(int id,int v){
	tr[id]+=v,tag[id]+=v;
}
il void pushdown(int id){
	if(tag[id]){
		if(!ls[id]){
			ls[id]=++cnt;
			tr[cnt]=tag[cnt]=0;
			ls[cnt]=rs[cnt]=0;
		}
		if(!rs[id]){
			rs[id]=++cnt;
			tr[cnt]=tag[cnt]=0;
			ls[cnt]=rs[cnt]=0;
		}
		pushtag(ls[id],tag[id]);
		pushtag(rs[id],tag[id]);
		tag[id]=0;
	}
}
il void upd(int &id,int L,int R,int l,int r,int v){
	if(!id){
		id=++cnt;
		tr[id]=tag[id]=0;
		ls[id]=rs[id]=0;
	}
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		upd(ls[id],L,mid,l,r,v);
	}
	if(r>mid){
		upd(rs[id],mid+1,R,l,r,v);
	}
	pushup(id);
}
il void solve(){
	int tot=0;
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].c);
		lsh[++tot]=a[i].y;
	}
	sort(lsh+1,lsh+n+1);
	tot=unique(lsh+1,lsh+n+1)-lsh-1;
	for(int i=1;i<=n;i++){
		cun[lwrb(lsh+1,lsh+tot+1,a[i].y)-lsh].pb(i);
	}
	int ans=0;
	cnt=rt=0;
	for(int i=1,p=1,q;i<=tot;i++){
		for(int j:cun[i]){
			upd(rt,0,1ll<<31,a[j].x,a[j].x+W-1,a[j].c);
		}
		q=lwrb(lsh+1,lsh+tot+1,lsh[i]-H+1)-lsh-1;
		while(p<=q){
			for(int j:cun[p]){
				upd(rt,0,1ll<<31,a[j].x,a[j].x+W-1,-a[j].c);
			}
			p++;
		}
		ans=max(ans,tr[rt]);
	}
	printf("%lld\n",ans);
	for(int i=1;i<=tot;i++){
		cun[i].clear();
	}
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
//	cout<<cplx::usdmem();
	while(~scanf("%lld%lld%lld",&n,&W,&H)){
//		puts("666");
		solve();
	}
	return 0;
}
}
signed main(){return asbt::main();}
posted @ 2025-02-06 21:43  zhangxy__hp  阅读(42)  评论(0)    收藏  举报