扫描线入门

来源:互联网 发布:centos 光盘 yum源 编辑:程序博客网 时间:2024/05/21 17:27

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


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


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


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

Weird AdvertisementUVA - 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 longusing 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 longusing 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