[BZOJ4422][Cerc2015]Cow Confinement(扫描线+线段树)

来源:互联网 发布:阿里云网站建设方案 编辑:程序博客网 时间:2024/06/05 16:08

=== ===

这里放传送门

=== ===

题解

记得这题很久以前学长出过胡策。。然后当时没做出来。。然后照着题解打了一发然后怎么调怎么WA然后就弃了。。当时的题解好像是用差分什么的?基本思路是对每个点维护(i,j)可以到但是(i+1,j)到不了的花的数量,那么对于一个牛来说统计答案就是找到它下面最靠近它的一个栅栏然后把这一段全加起来,遇到一个栅栏就把那一段清零然后把贡献转移到栅栏上面的点之类这样的处理。。
然后一段时间以后ATP再捡起来这个题的时候除了知道这个题是扫描线以外已经完全忘了题解怎么写的然后就强行搞一个不用差分的做法。。。

首先我们需要考虑如何统计答案,要统计对于所有牛的答案,如何分清楚哪一段应该加到哪个牛上的呢?每次修改的时候涉及的牛的编号显然不是连续的,往这个方面考虑肯定不行。每次修改的时候暴力枚举所有牛显然也不是很可以。。那肯定就是在扫描线碰到它的时候就统计它的答案咯?
那这样的话就要求在扫描线碰到某个牛的时候它有可能走到的花已经全都加到线段树里面了,可以确定是从右往左扫描线。

然后就是考虑如何对每一个点维护它能到达的花的数目了。扫描线用线段树来维护,那么线段树里应该存储的就是这一列所有点能到达的花的数目。于是考虑每一种事件会造成的影响,如果来了一个花,那么它往上一直走,直到被一个栅栏挡住为止,路上的所有点都能通过向下走来达到它。那么就找到它上面的第一个栅栏,记为pos,然后对于pos+1..now这一段都+1就可以了。
举个例子来说,下面这个图:
这里写图片描述
1号花能够控制(1,4)和(2,1)这两个格子,因为它往上找的第一个栅栏是那个栅栏的上边界。而2号花能控制(4,4),(5,4)这两个格子,显然它往上找的第一个格子是下边界的那个位置。
注意的细节问题就是因为这个题是格子,而栅栏是边框,所以搞的时候稍微有点麻烦,因为要找到上面最靠近它的栅栏,为了正确进行覆盖所以记录上边界的时候记录的不是up而是up-1。比如说上面那个图下边界存的是3,而上边界存的不是1而是0。可以发现这样的话当一个花被栅栏上边界挡住,和下边界一样处理,覆盖pos+1..now,就能正确统计答案。

关键就是对栅栏的处理。如果遇到一个栅栏的右边界,那么说明现在要进入栅栏了,所以栅栏内部那些点都走不到外面了,就要把栅栏覆盖的那一段清零。如果遇到一个栅栏的左边界,说明现在要出栅栏了,那么原来被栅栏覆盖的那些位置现在都可以出去了,但是不能再进入栅栏内部了。所以还要再把那些位置清一遍,目的是把在栅栏内部累加的贡献清掉。然后就是出了栅栏以后那些原来被栅栏覆盖的位置可以使劲往下走然后走到原来那个栅栏的下面,那么就需要查询一下原来栅栏下面那个位置能走到多少花,然后把这一部分贡献给加上。

然而这里就出现了这种做法产生的一个比较重要的细节问题。在遇到一个栅栏的左边界准备出栅栏的时候要查询原来栅栏下面那个位置的答案然后累加上去,但是这一部分答案实际上分为两类,一类是在遇到这个栅栏之前就有的,还有一部分是遇到这个栅栏之后才被加进去的,就是被堵在这个栅栏的下边界的。对于第一类贡献,栅栏上面的那些点已经累加过了,所以只需要把栅栏那一块覆盖了就可以了;但是第二类贡献没有在栅栏上面累加过,所以需要一直往上找到能够堵住它的第一个栅栏把这一块全都覆盖掉。所以对每个栅栏要记录一下它下面堵住了多少花,在这个栅栏消失的时候要对两类贡献分类讨论一下。

最后就是处理顺序的问题,一开始ATP写了一个错的东西就是直接把栅栏左右边界读进来排序然后按照右边界->花->牛->左边界这样的顺序处理,但是就有这种情况没办法处理:
这里写图片描述
如果按照ATP一开始那种做法的话它会先加入第二个栅栏的右边界,然后把花加进去,然后碰到了左边界把第二个栅栏消掉。但是这个时候第一个栅栏还没有加进去,然后那个花的贡献会一直累加到最上面,统计的时候那个牛就会觉得自己能碰到那个花。但是因为有第一个栅栏挡着所以实际上它是碰不到的。
为了解决这个问题就需要把栅栏的左右边界搞成左闭右开区间,也就是把右边界+1再扔进去排序,然后按照花->牛->右边界->左边界的顺序处理,这样就能解决上面那种情况了。

这方法极其麻烦然后ATP说的也极其啰嗦。。能坚持看到这里的真是辛苦了。。。。。。。
最后来一个图模拟一下ATP的做法:
这里写图片描述

代码

#include<cstdio>#include<cstring>#include<algorithm>#define inf 1000000000#define Lim 1000000using namespace std;int F,M,N,sum[4000010],cov[4000010],dlt[4000010],s[800010],hash[1000010],n,cnt,ans[200010],down[400010],f[1000010];struct query{    int x,y,yy,type;    query(int X=0,int Y=0,int YY=0,int T=0){x=X;y=Y;yy=YY;type=T;}}q[800010];int comp(query a,query b){    return a.x>b.x||(a.x==b.x&&a.type<b.type);}void Cov(int i,int l,int r,int c){    dlt[i]=0;sum[i]=c*(r-l+1);cov[i]=c;}void Add(int i,int l,int r,int val){    if (cov[i]<inf){Cov(i,l,r,cov[i]+val);return;}    sum[i]+=val*(r-l+1);dlt[i]+=val;}void pushdown(int i,int l,int r){    int mid=(l+r)>>1;    if (dlt[i]!=0){        Add(i<<1,l,mid,dlt[i]);        Add((i<<1)+1,mid+1,r,dlt[i]);        dlt[i]=0;    }    if (cov[i]<inf){        Cov(i<<1,l,mid,cov[i]);        Cov((i<<1)+1,mid+1,r,cov[i]);        cov[i]=inf;    }}void cover(int i,int l,int r,int left,int right,int val){    if (left<=l&&right>=r){Cov(i,l,r,val);return;}    int mid=(l+r)>>1;    pushdown(i,l,r);    if (left<=mid) cover(i<<1,l,mid,left,right,val);    if (right>mid) cover((i<<1)+1,mid+1,r,left,right,val);    sum[i]=sum[i<<1]+sum[(i<<1)+1];}void addval(int i,int l,int r,int left,int right,int val){    if (left<=l&&right>=r){Add(i,l,r,val);return;}    int mid=(l+r)>>1;    pushdown(i,l,r);    if (left<=mid) addval(i<<1,l,mid,left,right,val);    if (right>mid) addval((i<<1)+1,mid+1,r,left,right,val);    sum[i]=sum[i<<1]+sum[(i<<1)+1];}int ask(int i,int l,int r,int x){    if (l==r) return sum[i];    int mid=(l+r)>>1;    pushdown(i,l,r);    if (x<=mid) return ask(i<<1,l,mid,x);    else return ask((i<<1)+1,mid+1,r,x);}int lowbit(int i){return i&(-i);}void addpos(int i,int v){    while (i<=n){s[i]+=v;i+=lowbit(i);}}int ask(int i){    int ans=0;    while (i!=0){ans+=s[i];i-=lowbit(i);}    return ans;}int getpos(int l,int r,int x){    int mid;    while (l!=r){        mid=(l+r)>>1;        if (ask(x)-ask(mid)==0) r=mid;        else l=mid+1;    }    return l;}int main(){    scanf("%d",&F);    for (int i=1;i<=F;i++){        int x,y,xx,yy;        scanf("%d%d%d%d",&x,&y,&xx,&yy);        swap(x,y);swap(xx,yy);++xx;//注意要把右边界扩大一个位置        q[++cnt]=query(x,y,yy,i+3);        q[++cnt]=query(xx,y,yy,-i);        hash[++n]=y;hash[++n]=yy;hash[++n]=y-1;     }//离散化的时候要把上边界的上一个位置也统计进去    scanf("%d",&M);    for (int i=1;i<=M;i++){        int x,y;scanf("%d%d",&x,&y);        swap(x,y);hash[++n]=y;        q[++cnt]=query(x,y,0,-inf);    }    scanf("%d",&N);    for (int i=1;i<=N;i++){        int x,y;scanf("%d%d",&x,&y);        swap(x,y);hash[++n]=y;        q[++cnt]=query(x,y,i,-inf+1);    }    sort(hash+1,hash+n+1);n=unique(hash+1,hash+n+1)-hash-1;    for (int i=1;i<=cnt;i++){        q[i].y=lower_bound(hash+1,hash+n+1,q[i].y)-hash;        if (q[i].type!=-inf&&q[i].type!=-inf+1)          q[i].yy=lower_bound(hash+1,hash+n+1,q[i].yy)-hash;    }    memset(cov,127,sizeof(cov));    sort(q+1,q+cnt+1,comp);++n;    for (int i=1;i<=cnt;){        int rec=q[i].x;        while (i<=cnt&&q[i].x==rec&&q[i].type==-inf){//先处理花            int pos=getpos(0,q[i].y-1,q[i].y-1);//找到上面最靠近它的那个栅栏的位置            addval(1,1,n,pos+1,q[i].y,1);            if (f[pos]>0) down[f[pos]]++;//存储有多少花被堵在了这个栅栏的下边界            ++i;        }        while (i<=cnt&&q[i].x==rec&&q[i].type==-inf+1){            ans[q[i].yy]=ask(1,1,n,q[i].y);++i;        }        while (i<=cnt&&q[i].x==rec&&q[i].type<0){            cover(1,1,n,q[i].y,q[i].yy,0);//把前面的贡献清零            if (q[i].y!=1) addpos(q[i].y-1,1);            addpos(q[i].yy,1);//把栅栏边界的位置加入树状数组            f[q[i].y-1]=q[i].type;//记录这个位置的栅栏编号            f[q[i].yy]=-q[i].type;            ++i;        }        while (i<=cnt&&q[i].x==rec&&q[i].type>=4){            int val=ask(1,1,n,q[i].yy+1),pos,dlt;            dlt=down[q[i].type-3];//查出有多少花被堵在了下边界            cover(1,1,n,q[i].y,q[i].yy,0);//把栅栏内部的贡献清零            if (val!=dlt)//把原来就有的那些贡献加到栅栏范围内              addval(1,1,n,q[i].y,q[i].yy,val-dlt);            if (q[i].y!=1)addpos(q[i].y-1,-1);            addpos(q[i].yy,-1);//把这个位置的栅栏删掉            f[q[i].y-1]=f[q[i].yy]=0;            if (dlt!=0){//累加那些原来被堵在下面的贡献                pos=getpos(0,q[i].yy,q[i].yy);                addval(1,1,n,pos+1,q[i].yy,dlt);                if (f[pos]>0) down[f[pos]]+=dlt;            }++i;        }    }    for (int i=1;i<=N;i++) printf("%d\n",ans[i]);    return 0;}
0 0
原创粉丝点击