题意

一个10610^6106行10610^6106列的网格图,上面有一些牛、花和一些矩形围栏,围栏在格子的边界上,牛和花在格子里,牛只能向下或向右走,不能穿过围栏和地图边界,求每头牛它能到达的花的数量。(栅栏不会相交)

题解

暴力

首先,可以想出一个很显然的dp,设dpi,jdp_{i,j}dpi,j​表示方格(i,j)(i,j)(i,j)能到达的花数。要分类讨论,前三种情况很简单,再次不赘述,特别地,有一种情况不能忽略,如下图:(红色范围为栏杆,蓝色为当前格)

如果dpi,j=dpi+1,j+dpi,j+1dp_{i,j}=dp_{i+1,j}+dp_{i,j+1}dpi,j​=dpi+1,j​+dpi,j+1​,会发现黑色部分重复计算,需要减去围栏左下角的dpdpdp值。

时间复杂度O(n2)O(n^2)O(n2),需要优化

优化

我们发现除了有围栏遮挡的情况外,当前状态的更新都用到dpi+1,jdp_{i+1,j}dpi+1,j​和dpi,j+1dp_{i,j+1}dpi,j+1​,可以考虑差分
我们可以按yyy从大往小扫描线,维护数组fif_ifi​,表示第y列中(注意题目中的y是横坐标)dpi,y−dpi+1,ydp_{i,y}-dp_{i+1,y}dpi,y​−dpi+1,y​的值。可以画个图帮助理解:

如上图f1f_1f1​统计范围是蓝色部分,f2f_2f2​也是它对应的横条,f3f_3f3​统计围栏内,而f4f_4f4​则统计黄色部分,一个“7”形。
这样,要统计某一头牛(x,y)(x,y)(x,y)能到达的花,只需找出第一个挡住它的围栏横坐标(R),答案就是∑x≤i≤Rfi\sum_{x\leq i\leq R}f_i∑x≤i≤R​fi​

现在问题是当扫描完一条线,yyy值减一时,如何维护fif_ifi​?同样要分类讨论。
1.遇到花,直接在对应位置把fif_ifi​加一即可
2.遇到牛,更新答案
3.遇到栏杆的开始:设栏杆纵坐标为yyy,横坐标范围为lll到rrr。首先需要把fl−1f_{l-1}fl−1​加上旧的∑l≤i≤rfi\sum_{l\leq i\leq r}f_i∑l≤i≤r​fi​(即上图黄色图形的下边三格),然后把flf_lfl​至frf_rfr​设为0(因为出不去),再把lll,rrr标记为围栏(更新答案要用)
4.遇到围栏的结束:设栏杆纵坐标为yyy,横坐标范围为lll到rrr。需要把fl−1f_{l-1}fl−1​减去重复部分(像dp第4种情况一样,即上图黄色图形的下边两格与f1,f2f_1,f_2f1​,f2​重复,重复部分需要在情况3记录),把flf_lfl​至frf_rfr​设为0(因为进不去围栏),再把lll,rrr删除围栏标记

综上所述,我们要对fif_ifi​进行单点修改,区间复制,区间求和,建立标记和查询后一个标记。可以用线段树维护。
(水平有限,表达可能不清,请结合代码)

代码

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#define db double
#define ll long long
#define inf 1000005
#define eps 1e-6
#define INF 2005
char buf[10000001],*Hd,*Tl;
#define getchar(ch) {if (Hd==Tl){int len=fread(buf,1,10000000,stdin);Hd=buf,Tl=Hd+len;}ch=(*Hd++);}
//#define getchar(ch) ch=getchar()
#define rd(n) {n=0;char ch;int f=0;do{getchar(ch);if(ch=='-'){f=1;}}while(ch<'0'||ch>'9');while('0'<=ch&&ch<='9'){n=(n<<1)+(n<<3)+ch-48;getchar(ch);}if(f)n=-n;}
using namespace std;inline void write(int x){char c=x%10+48;if (x<10){putchar(c);return;}write(x/10);putchar(c);return;
}struct ST{int sum,set,fen;
}t[inf*4];void ST_pushdown(int u){if (t[u].set!=-1){t[u*2+1].set=t[u*2].set=0;t[u*2+1].sum=t[u*2].sum=0;t[u].set=-1;}return;
}void ST_pushup(int u){t[u].sum=t[u*2].sum+t[u*2+1].sum;t[u].fen=(t[u*2].fen|t[u*2+1].fen);return;
}void ST_build(int u,int l,int r,int N){t[u].set=-1;if (l==r){if (l==N){t[u].fen=1;}return;}int mid=(l+r)/2;ST_build(u*2,l,mid,N);ST_build(u*2+1,mid+1,r,N);ST_pushup(u);return;
}void ST_add(int u,int l,int r,int k,int v){if (l==r){t[u].sum+=v;return;}ST_pushdown(u);int mid=(l+r)/2;if (k<=mid){ST_add(u*2,l,mid,k,v);}else{ST_add(u*2+1,mid+1,r,k,v);}ST_pushup(u);return;
}void ST_set0(int u,int l,int r,int L,int R){if (L<=l && r<=R){t[u].set=0;t[u].sum=0;return;}ST_pushdown(u);int mid=(l+r)/2;if (L<=mid){ST_set0(u*2,l,mid,L,R);}if (R>mid){ST_set0(u*2+1,mid+1,r,L,R);}ST_pushup(u);return;
}void ST_setfen(int u,int l,int r,int k){if (l==r){t[u].fen^=1;return;}int mid=(l+r)/2;ST_pushdown(u);if (k<=mid){ST_setfen(u*2,l,mid,k);}else{ST_setfen(u*2+1,mid+1,r,k);}ST_pushup(u);return;
}int ST_nxtfen(int u,int l,int r,int k){if (k<=l){if (!t[u].fen){return 0;}while (l!=r){int mid=(l+r)/2;if (t[u*2].fen){u=u*2,r=mid;}else{u=u*2+1,l=mid+1;}}return l;}ST_pushdown(u);int mid=(l+r)/2;if (k<=mid){int ans=ST_nxtfen(u*2,l,mid,k);return ans?ans:ST_nxtfen(u*2+1,mid+1,r,k);}return ST_nxtfen(u*2+1,mid+1,r,k);
}int ST_query(int u,int l,int r,int L,int R){if (L<=l && r<=R){return t[u].sum;}ST_pushdown(u);int mid=(l+r)/2,ans=0;if (L<=mid){ans+=ST_query(u*2,l,mid,L,R);}if (R>mid){ans+=ST_query(u*2+1,mid+1,r,L,R);}return ans;
}struct line{int l,r,y;int id,p;line(){}line(int x1,int x2,int yy,int ii,int pp){l=x1,r=x2,y=yy,id=ii,p=pp;}
}L[inf];bool operator<(line _1,line _2){if (_1.y==_2.y){return _1.l<_2.l;}return _1.y>_2.y;
}struct point{int x,y;int id;
}C[inf],F[inf];bool operator<(point _1,point _2){return _1.y>_2.y;
}int lcnt,ccnt,fcnt,N;
int ans[inf],s[inf];void addline(int id){if (L[id].p==0){int nxt=ST_nxtfen(1,1,N,L[id].r);s[L[id].id]=ST_query(1,1,N,L[id].r+1,nxt);int sum=ST_query(1,1,N,L[id].l,L[id].r);ST_set0(1,1,N,L[id].l,L[id].r);if (L[id].l!=1){ST_add(1,1,N,L[id].l-1,sum+s[L[id].id]);ST_setfen(1,1,N,L[id].l-1);}ST_setfen(1,1,N,L[id].r);}else{ST_set0(1,1,N,L[id].l,L[id].r);if (L[id].l!=1){ST_add(1,1,N,L[id].l-1,-s[L[id].id]);ST_setfen(1,1,N,L[id].l-1);}ST_setfen(1,1,N,L[id].r);}return;
}int main(){int cnt,x1,x2,y1,y2;rd(cnt)for (int i=1;i<=cnt;i++){rd(x1) rd(y1) rd(x2) rd(y2)L[i*2]=line(x1,x2,y2,i,0);L[i*2-1]=line(x1,x2,y1-1,i,1);N=max(N,x2);}lcnt=cnt*2;rd(fcnt)for (int i=1;i<=fcnt;i++){rd(F[i].x) rd(F[i].y)N=max(N,F[i].x);}rd(ccnt)for (int i=1;i<=ccnt;i++){rd(C[i].x) rd(C[i].y) N=max(N,C[i].x);C[i].id=i;}sort(L+1,L+lcnt+1);sort(C+1,C+ccnt+1);sort(F+1,F+fcnt+1);N=inf-4;ST_build(1,1,N,N);int li=1,ci=1,fi=1;for (int i=N;i>=1;i--){for (;L[li].y==i;li++){addline(li);}for (;F[fi].y==i;fi++){ST_add(1,1,N,F[fi].x,1);}for (;C[ci].y==i;ci++){int nxt=ST_nxtfen(1,1,N,C[ci].x);ans[C[ci].id]=ST_query(1,1,N,C[ci].x,nxt);}}for (int i=1;i<=ccnt;i++){write(ans[i]);putchar('\n');}return 0;
}

【BZOJ4422】Cow Confinement【扫描线】【差分】【线段树】相关推荐

  1. 850. 矩形面积 II:扫描线+离散化+线段树

    Difficulty: hard 标签: 扫描线, 离散化, 线段树 题目链接 力扣 题目解析 面试代码 /** x轴方向使用扫描线,y轴方向使用线段树维护扫描线的长度和每个区间覆盖的次数.由于y轴方 ...

  2. 洛谷 - P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并(树上差分+线段树合并)

    题目链接:点击查看 题目大意:给出一棵树,再给出 m 次操作,每次操作会选择两个点 ( x , y ) ,使得这条路径上的所有点的种类 z 加一,最后问每个点的哪个种类出现的频率最高,若多个种类出现频 ...

  3. Atlantis HDU - 1542 (扫描线,线段树)

    扫描线的模板题,先把信息接收,然后排序,记录下上边和下边,然后用一条虚拟的线从下往上扫.如果我扫到的是下边,那么久用线段树在这个区间内加上1,表示这个区间现在是有的,等我扫描到上边的时候在加上-1,把 ...

  4. C2 - Pokémon Army (hard version)(思维+差分/线段树+dp)详解

    https://codeforces.com/contest/1420/problem/C2 这道题十分的锻炼思维,也让我知道了同样是差分,从前面减后面和从后面减前面是有不同的意义的. 还记得c1吗? ...

  5. hdu6681 Rikka with Cake(扫描线,线段树)

    题意: 在n*m的坐标系平面上有k条射线,射线有上下左右四种不同的方向, 问这些射线把这个平面切成多少块? 坐标系的左下角为(0,0),右上角为(n,m). 数据范围:n,m<=1e9,k< ...

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

    === === 这里放传送门 === === 题解 记得这题很久以前学长出过胡策..然后当时没做出来..然后照着题解打了一发然后怎么调怎么WA然后就弃了..当时的题解好像是用差分什么的?基本思路是对每 ...

  7. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解

    题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...

  8. [矩形并-扫描线-线段树]Picture

    最近在补数学和几何,没啥好写的,因为已经决定每天至少写一篇了,今天随便拿个题水水. 题目大意:给你N个边平行于坐标轴的矩形,求它们并的周长.(N<=5000) 思路:这个数据范围瞎暴力就过了,但 ...

  9. poj 1177 线段树+离散化+扫描线 求矩形并的轮廓长

    Picture 题意:求矩形面积并额轮廓长 解法:扫描线+离散化+线段树 做法:等于更新操作前后的tree[1].len差,做法这么巧妙实际我也不知道为什么.差不多的意思就是更新操作的cover变化长 ...

  10. 刷题总结——影魔(HNOI2017 BZOJ4826 线段树+扫描线)

    题目: Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样 的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄 ...

最新文章

  1. 单阶段6D对象姿势估计
  2. 论前端工程师如何应对西电教学评估系统
  3. 与老大的交谈——估算项目时间
  4. 深度解析PHP数组函数array_chunk
  5. sentinel 限流熔断神器详细介绍
  6. ITK:将BinaryMorphologicalClosingFilter应用于给定LabelMap的一个LabelObject
  7. linux下eclipse+pdt(PHP集成开发环境安装)
  8. 机器学习模型 知乎_机器学习-模型选择与评价
  9. 漫画 | 面试的我 VS 真实的我
  10. png-CRC32校验
  11. C语言课后习题(38)
  12. React Native屏幕尺寸适配
  13. 帖子如何实现显示浏览次数_我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程...
  14. 关于Qt学习之路2:8、添加动作 这一节课 发现里面的程序运行后没有图标
  15. C++fseek函数
  16. IDC发布最新中国AI云服务市场报告,百度智能云连续三次排名第一
  17. Android TextView设置下划线
  18. python读取页眉页脚,python批量替换页眉页脚
  19. 中外消防传感器差距浅析
  20. Warning: findDOMNode is deprecated in StrictMode

热门文章

  1. vb.net 图片水平翻转_公务员行测图形推理考点:位置类考点“翻转”【秒杀技巧】...
  2. OpenGL编程低级错误 + 常见问题解答
  3. 景观生态学原理| 6 景观生态分类与评价
  4. 从Python安装到语法基础,小白都能懂的爬虫教程!(附代码)
  5. alembic 如何使用?
  6. 数字化首个安全生产标准!阿里云混合云联合信通院发布《基于云计算的数字化业务安全工程要求》
  7. OpenCV获取视频帧
  8. matlab中双引号_MATLAB的21种特殊符号,总有一种你不知道的用法
  9. 云存储,甩动数字时代的“牛鞭”
  10. Think Before You Speak: Explicitly Generating Implicit Commonsense Knowledge for Response Generation