=== ===

这里放传送门

=== ===

题解

记得这题很久以前学长出过胡策。。然后当时没做出来。。然后照着题解打了一发然后怎么调怎么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 1000000
using 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;
}

[BZOJ4422][Cerc2015]Cow Confinement(扫描线+线段树)相关推荐

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

    很容易发现一个O(n2)DP,f[i][j]=f[i][j+1]+f[i+1][j]-f[i+1][j+1].然后由于有栅栏,一些位置没办法走,然后就可以用类似差分的方法,f[i]表示当前行f[i+1 ...

  2. 【BZOJ-4422】Cow Confinement 线段树 + 扫描线 + 差分 (优化DP)

    4422: [Cerc2015]Cow Confinement Time Limit: 50 Sec  Memory Limit: 512 MB Submit: 61  Solved: 26 [Sub ...

  3. Bzoj 4422: [Cerc2015]Cow Confinement(线段树+扫描线)

    以下内容来自ShallWe's Blog 题目 题目链接 4422: [Cerc2015]Cow Confinement Description 一个10^6行10^6列的网格图,上面有一些牛.花和一 ...

  4. 【HDU5091】Beam Cannon,扫描线+线段树

    传送门 思路: 扫描线的经典问题 然而并不是很会做-- 对x坐标差分一下(例如(x,y)类型为1,(x+w,y)的类型就是-1,以便于之后扫描可以去除该点),然后对y坐标转化为正数(便于操作为线段树上 ...

  5. 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树

    [BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...

  6. 扫描线+线段树简介 AcWing 248窗内的星星题解

    ----出自南昌理工学院ACM集训队 这周学习了线段树和扫描线的解题方法,下面由小菜鸡简介一下: 一般扫描线的题目最简单的便是扫描线裸模板(一般来说的话:数据范围小),其次的话便是进行拓展成线段树+扫 ...

  7. 【BZOJ4422】Cow Confinement【扫描线】【差分】【线段树】

    题意 一个10610^6106行10610^6106列的网格图,上面有一些牛.花和一些矩形围栏,围栏在格子的边界上,牛和花在格子里,牛只能向下或向右走,不能穿过围栏和地图边界,求每头牛它能到达的花的数 ...

  8. POJ 1177 Picture [离散化+扫描线+线段树]

    http://poj.org/problem?id=1177 给若干矩形,求被覆盖的区域的周长. 将 y 坐标离散化后,按 x 坐标进行扫描.用线段树维护两个东西,当前竖线的叠加长度 len 和 条数 ...

  9. hdu 4419 Colourful Rectangle (离散化扫描线线段树)

    Problem - 4419 题意不难,红绿蓝三种颜色覆盖在平面上,不同颜色的区域相交会产生新的颜色,求每一种颜色的面积大小. 比较明显,这题要从矩形面积并的方向出发.如果做过矩形面积并的题,用线段树 ...

最新文章

  1. 【转】几种页面重定向代码总结
  2. 爷青回!GAN生成的超级马里奥关卡,可以永不通关的那种
  3. Android Weekly Notes Issue #226
  4. zip 密码算法 java,java zip 密码
  5. qt连接错误ip的sqlserver超时时间_参数设置导致请求超时案例
  6. struts+hibernate+oracle+easyui实现lazyout组件的简单案例——Jsp页面
  7. 哈佛博士生经验分享:10种方法让你在读博阶段更加轻松愉快
  8. Serverless 实战 —— 阿里云函数计算配合SpringBoot项目
  9. 千兆交换机下面可以接多少层交换机_视频监控系统如何选择网络交换机
  10. “微软高管”拯救必应 搞定盖茨成为合伙人?官方打脸:实为开除员工
  11. 一道算法题:等价类思想在计数中的应用
  12. 我所想的GIX4的权限
  13. 为全面到来的数字化未来准备就绪 戴尔科技峰会赋能企业数字化发展新动力
  14. 阶段1 语言基础+高级_1-3-Java语言高级_08-JDK8新特性_第1节 常用函数接口_7_常用的函数式接口_Supplier接口...
  15. 【三维路径规划】基于matlab A_star算法无人机三维路径规划【含Matlab源码 1387期】
  16. 时间管理(二):时间管理的六项基本原则
  17. shark恒破解笔记4-API断点GetPrivateProfileStringA
  18. CAD打印 acad.ctb丢失
  19. 探讨大数据时代如何规划智慧城市
  20. lucene java 庖丁解牛_Lucene分词器之庖丁解牛

热门文章

  1. EFM32例程——EMU
  2. 精华笔记 112314
  3. 中国智能医疗行业十四五规划趋势与发展战略分析报告2022-2027年版
  4. 如何在Windows 10中设置和测试麦克风
  5. 尚硅谷Vue2学习笔记 (二)
  6. excel服务器库存管理系统,非常实用的Excel库存管理系统
  7. 定位详解(固定定位,粘滞定位)
  8. javaweb后台如何传json数据给前台,以及前台如何解析得到json中数据
  9. 基于单片机温湿度PT100热敏传感器检测仿真-毕设课设资料
  10. 【时间同步】NTP还是PTP?