【BZOJ4422】Cow Confinement【扫描线】【差分】【线段树】
题意
一个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≤Rfi
现在问题是当扫描完一条线,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≤rfi(即上图黄色图形的下边三格),然后把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【扫描线】【差分】【线段树】相关推荐
- 850. 矩形面积 II:扫描线+离散化+线段树
Difficulty: hard 标签: 扫描线, 离散化, 线段树 题目链接 力扣 题目解析 面试代码 /** x轴方向使用扫描线,y轴方向使用线段树维护扫描线的长度和每个区间覆盖的次数.由于y轴方 ...
- 洛谷 - P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并(树上差分+线段树合并)
题目链接:点击查看 题目大意:给出一棵树,再给出 m 次操作,每次操作会选择两个点 ( x , y ) ,使得这条路径上的所有点的种类 z 加一,最后问每个点的哪个种类出现的频率最高,若多个种类出现频 ...
- Atlantis HDU - 1542 (扫描线,线段树)
扫描线的模板题,先把信息接收,然后排序,记录下上边和下边,然后用一条虚拟的线从下往上扫.如果我扫到的是下边,那么久用线段树在这个区间内加上1,表示这个区间现在是有的,等我扫描到上边的时候在加上-1,把 ...
- C2 - Pokémon Army (hard version)(思维+差分/线段树+dp)详解
https://codeforces.com/contest/1420/problem/C2 这道题十分的锻炼思维,也让我知道了同样是差分,从前面减后面和从后面减前面是有不同的意义的. 还记得c1吗? ...
- hdu6681 Rikka with Cake(扫描线,线段树)
题意: 在n*m的坐标系平面上有k条射线,射线有上下左右四种不同的方向, 问这些射线把这个平面切成多少块? 坐标系的左下角为(0,0),右上角为(n,m). 数据范围:n,m<=1e9,k< ...
- [BZOJ4422][Cerc2015]Cow Confinement(扫描线+线段树)
=== === 这里放传送门 === === 题解 记得这题很久以前学长出过胡策..然后当时没做出来..然后照着题解打了一发然后怎么调怎么WA然后就弃了..当时的题解好像是用差分什么的?基本思路是对每 ...
- 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解
题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...
- [矩形并-扫描线-线段树]Picture
最近在补数学和几何,没啥好写的,因为已经决定每天至少写一篇了,今天随便拿个题水水. 题目大意:给你N个边平行于坐标轴的矩形,求它们并的周长.(N<=5000) 思路:这个数据范围瞎暴力就过了,但 ...
- poj 1177 线段树+离散化+扫描线 求矩形并的轮廓长
Picture 题意:求矩形面积并额轮廓长 解法:扫描线+离散化+线段树 做法:等于更新操作前后的tree[1].len差,做法这么巧妙实际我也不知道为什么.差不多的意思就是更新操作的cover变化长 ...
- 刷题总结——影魔(HNOI2017 BZOJ4826 线段树+扫描线)
题目: Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样 的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄 ...
最新文章
- 单阶段6D对象姿势估计
- 论前端工程师如何应对西电教学评估系统
- 与老大的交谈——估算项目时间
- 深度解析PHP数组函数array_chunk
- sentinel 限流熔断神器详细介绍
- ITK:将BinaryMorphologicalClosingFilter应用于给定LabelMap的一个LabelObject
- linux下eclipse+pdt(PHP集成开发环境安装)
- 机器学习模型 知乎_机器学习-模型选择与评价
- 漫画 | 面试的我 VS 真实的我
- png-CRC32校验
- C语言课后习题(38)
- React Native屏幕尺寸适配
- 帖子如何实现显示浏览次数_我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程...
- 关于Qt学习之路2:8、添加动作 这一节课 发现里面的程序运行后没有图标
- C++fseek函数
- IDC发布最新中国AI云服务市场报告,百度智能云连续三次排名第一
- Android TextView设置下划线
- python读取页眉页脚,python批量替换页眉页脚
- 中外消防传感器差距浅析
- Warning: findDOMNode is deprecated in StrictMode
热门文章
- vb.net 图片水平翻转_公务员行测图形推理考点:位置类考点“翻转”【秒杀技巧】...
- OpenGL编程低级错误 + 常见问题解答
- 景观生态学原理| 6 景观生态分类与评价
- 从Python安装到语法基础,小白都能懂的爬虫教程!(附代码)
- alembic 如何使用?
- 数字化首个安全生产标准!阿里云混合云联合信通院发布《基于云计算的数字化业务安全工程要求》
- OpenCV获取视频帧
- matlab中双引号_MATLAB的21种特殊符号,总有一种你不知道的用法
- 云存储,甩动数字时代的“牛鞭”
- Think Before You Speak: Explicitly Generating Implicit Commonsense Knowledge for Response Generation