扫描线入门

听说扫描线很牛掰,于是就见识了一下。


之前做过一道扫描线的题,brother,就是判断矩形是否被矩形内部的车攻击到。当时是把矩形拆成出边和入边(竖直的),把所有小于x2的车加进图中,扫[y1,y2]中x最小的车的x.和x1判断比较。然后交换x,y扫一遍


当时只是感觉线段树很神奇,还可以这样搞,后来才知道这种按x2的排序遍历就相当于扫描线。


然后就做了扫描线的入门题。

Weird Advertisement

UVA - 11983

一道扫描线矩形面积并,求被覆盖k次的矩形面积(虽然是点,不过x2+1,y2+1就变成了求面积。  比如只有一个点,把它x2+1,y2+1,就变成了面积为1的正方形)。

理解了大致思路,套了个版.


下面解决一下大致思路吧。


把矩形拆成入边和出边,记录下来,还要打上出入边标记。

按x排序(我是按x拆的,两条边x不同,竖直),然后扫到入边,把[y1,y2]加入,否则把[y1,y2]减去。

要更新>=k的次数。

其实主要复杂一点的是pushup.

一般需要离散化,因为题目往往不会很简单

覆盖K次矩形面积并

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define LL long long
using namespace std;
LL n,k;
const LL maxn=30000+20;
struct line
{
	LL x,y1,y2,flag;
	line(LL x,LL y1,LL y2,LL flag)
	{
		this->x=x;
		this->y1=y1;
		this->y2=y2;
		this->flag=flag;
	}
	bool operator<(const line &p)const
	{
		return x<p.x;
	}
};
vector<line>L;
LL det[maxn*2];
LL tot=0;
LL find(LL x)
{
	return lower_bound(det+1,det+tot+1,x)-det;
}
struct node
{
	LL l,r,flag;
	LL len[12];//长度为k的有多少个 
	void set(LL l,LL r,LL flag)
	{
		this->l=l;
		this->r=r;
		this->flag=flag;
	}
}T[8*maxn];
void pushup(LL u)
{
	LL l=T[u].l;
	LL r=T[u].r;
	for(LL i=0;i<=k;i++)T[u].len[i]=0;
	if(l==r)
	{
		LL t=min(T[u].flag,k);
		T[u].len[t]=det[r+1]-det[l];
	}
	else
	{
		for(LL i=0;i<=k;i++)
		{
			LL t=min(T[u].flag+i,k);
			T[u].len[t]+=T[u<<1].len[i]+T[u<<1|1].len[i];
		}
	}
}
void build(LL i,LL l,LL r)
{
	T[i].set(l,r,0);
	if(l==r)
	{
		pushup(i);
		return ;
	}
	LL mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	pushup(i);
}
void updata(LL i,LL L,LL R,LL x)
{
	LL l=T[i].l;
	LL r=T[i].r;
	if(l>=L&&r<=R)
	{
		T[i].flag+=x;
		pushup(i);
		return ;
	}
	LL mid=(l+r)>>1;
	if(L<=mid)updata(i<<1,L,R,x);
	if(R>mid)updata(i<<1|1,L,R,x);
	pushup(i);
}
int main()
{
	LL yu;
	scanf("%lld",&yu);
	for(LL cas=1;cas<=yu;cas++)
	{
		tot=0;
		L.clear();
		scanf("%lld%lld",&n,&k);
		for(LL i=1;i<=n;i++)
		{
			LL x1,y1,x2,y2;
			scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
			det[++tot]=y1;
			det[++tot]=y2+1;
			L.push_back(line(x1,y1,y2+1,1));
			L.push_back(line(x2+1,y1,y2+1,-1));
		}
		sort(L.begin(),L.end());
    	sort(det+1,det+tot+1);
    	LL t=tot;
    	tot=unique(det+1,det+t+1)-det-1;
	
    	build(1,1,tot);
    	LL ans=0;
    	for(LL i=0;i<L.size()-1;i++)
    	{
	     	LL l=find(L[i].y1);
	    	LL r=find(L[i].y2);
	    	updata(1,l,r-1,L[i].flag);
	    	ans+=(L[i+1].x-L[i].x)*T[1].len[k];
    	}
    	printf("Case %lld: %lld\n",cas,ans);		
	}
	
	return 0;
}


这是最裸的矩形面积并。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#define LL long long
using namespace std;
LL n;
const LL maxn=100000+20;
const LL k=1;
struct line
{
	LL x,y1,y2,flag;
	line(LL x,LL y1,LL y2,LL flag)
	{
		this->x=x;
		this->y1=y1;
		this->y2=y2;
		this->flag=flag;
	}
	bool operator<(const line&p)const
	{
		return x<p.x;
	}
};
vector<line>L;
LL det[maxn*2];
LL tot;
LL find(LL x)
{
	return lower_bound(det+1,det+tot+1,x)-det;
}
struct node
{
	LL l,r,flag;
	LL len[5];
	void set(LL l,LL r,LL flag)
	{
		this->l=l;
		this->r=r;
		this->flag=flag;
	}
}T[8*maxn];
void pushup(LL i)
{
	LL l=T[i].l;
	LL r=T[i].r;
	for(LL j=0;j<=k;j++)T[i].len[j]=0;
	if(l==r)
	{
		LL t=min(T[i].flag,k);
		T[i].len[t]=det[l+1]-det[l];
	}
	else 
	{
		for(LL j=0;j<=k;j++)
		{
			LL t=min(T[i].flag+j,k);
			T[i].len[t]+=T[i<<1].len[j]+T[i<<1|1].len[j];
		}		
	}
}
void build(LL i,LL l,LL r)
{
	T[i].set(l,r,0);
	if(l==r)
	{
		pushup(i);
		return ;
	}
	LL mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	pushup(i);
}
void updata(LL i,LL L,LL R,LL x)
{
	LL l=T[i].l;
	LL r=T[i].r;
	if(l>=L&&r<=R)
	{
		T[i].flag+=x;
		pushup(i);
		return ;
	}
	LL mid=(l+r)>>1;
	if(L<=mid)updata(i<<1,L,R,x);
	if(R>mid)updata(i<<1|1,L,R,x);
	pushup(i);
}
int main()
{
	freopen("rectangle.in","r",stdin);
	freopen("rectangle.out","w",stdout);
	tot=0;
	scanf("%I64d",&n);
	for(LL i=1;i<=n;i++)
	{
	    LL x1,y1,x2,y2;
		scanf("%I64d%I64d%I64d%I64d",&x1,&y1,&x2,&y2);
		L.push_back(line(x1,y1,y2,1));
		L.push_back(line(x2,y1,y2,-1));
		det[++tot]=y1;
		det[++tot]=y2;	
	}
	sort(det+1,det+tot+1);
	sort(L.begin(),L.end());
	LL t=tot;
	tot=unique(det+1,det+t+1)-det-1;
	build(1,1,tot);
	LL ans=0;
	for(LL i=0;i<L.size()-1;i++)
	{
		LL l=find(L[i].y1);
		LL r=find(L[i].y2);
		updata(1,l,r-1,L[i].flag);
		ans+=T[1].len[1]*(L[i+1].x-L[i].x);
	}
	printf("%I64d\n",ans);
	return 0;
}


扫描线大致是一种思想,用数据结构维护。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值