UVA 12663 第九届省赛 高桥与低桥 线段树
2014-03-12 17:09
323 查看
题意很简单,n个桥的高度是事先给出来的,然后有m次涨水与落水的高度,问有多少座桥在这m次涨落之后 被淹超过了k次,如果某桥本身被水淹了,此时再涨水,就不能算多淹一次
看下数据10的五次方,10的五次方的硬循环是避免不了了,很明显在计算被淹次数的时候要能缩到logN的复杂度才好。
于是想到先对桥由低到高排一下序,然后获取前一次的落水值和这次的涨水值,只要桥高度在涨落直接的必定是又被淹了一次。要多次对序列的某个区间进行加操作,自然是线段树了,确定区间的时候,就用二分,在进行线段树的时候 用懒惰标记
好久没写线段树了,这个题一写完就WA了,后来发现,首先二分出来的区间先要判断下来,若区间不存在就不要进行插入了,还有就是pushdown,我一开始是计划每次只增加1
,要进行下一次懒惰标记的时候先把上一次 的懒惰标记down下去,结果在pushdown函数里面没判断儿子的标记,如果儿子标记了 也得先down儿子啊。结果这样超时了,还是不能这样,索性就每次懒惰标记直接++,最后把整个标记down下去就行
发现其实d数组完全没有必要的,这个值没有必要维护,只是一开始写的时候没计划的太清,就这样写了。其实没必要。
最后为了避免某个懒惰标记未被down下去,最后还用个incase函数再走一遍线段树,把值给更新一下。
看下数据10的五次方,10的五次方的硬循环是避免不了了,很明显在计算被淹次数的时候要能缩到logN的复杂度才好。
于是想到先对桥由低到高排一下序,然后获取前一次的落水值和这次的涨水值,只要桥高度在涨落直接的必定是又被淹了一次。要多次对序列的某个区间进行加操作,自然是线段树了,确定区间的时候,就用二分,在进行线段树的时候 用懒惰标记
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define Lson (rt<<1),l,mid #define Rson (rt<<1|1),mid+1,r #define N 100010 using namespace std; int n,m,k; int d[N*3],maxk[N*3],flag[N*3],A ; void up(int rt) { d[rt]=d[rt<<1]+d[rt<<1|1]; maxk[rt]=maxk[rt<<1]+maxk[rt<<1|1]; } void pushdown(int rt,int l,int r) { if (l>=r || flag[rt]==0) return; int ll=rt<<1; int rr=rt<<1|1; int mid=(l+r)>>1; d[ll]+=flag[rt]; d[rr]+=flag[rt]; flag[ll]+=flag[rt]; flag[rr]+=flag[rt]; flag[rt]=0; if (mid-l+1==1 && d[ll]>=k) maxk[ll]=1; if (r-mid==1 && d[rr]>=k) maxk[rr]=1; // up(rt); } void add(int rt,int l,int r,int L,int R) { if (L<=l && r<=R) { pushdown(rt,l,r); d[rt]+=r-l+1; if (l>=r && d[rt]>=k) maxk[rt]=1; flag[rt]++; return; } pushdown(rt,l,r); int mid=(l+r)>>1; if (L<=mid) add(Lson,L,R); if (R>mid) add(Rson,L,R); up(rt); } void incase(int rt,int l,int r) { if (l>=r) { if(d[rt]>=k) maxk[rt]=1; return; } pushdown(rt,l,r); int mid=(l+r)>>1; incase(Lson); incase(Rson); up(rt); } void build(int rt,int l,int r) { flag[rt]=0; if(l>=r) { d[rt]=0; maxk[rt]=0; flag[rt]=0; return; } int mid=(l+r)>>1; build(Lson); build(Rson); up(rt); } int main() { int kase=0,a,b,cur; while (scanf("%d%d%d",&n,&m,&k)!=EOF) { for (int i=1;i<=n;i++){ scanf("%d",&A[i]); } sort(A+1,A+1+n); build(1,1,n); cur=1; for (int i=1;i<=m;i++) { scanf("%d%d",&a,&b); int l=upper_bound(A+1,A+1+n,cur)-A; int r=upper_bound(A+1,A+1+n,a)-A; r--; if (l<=r) add(1,1,n,l,r); cur=b; } incase(1,1,n); printf("Case %d: %d\n",++kase,maxk[1]); } return 0; }
好久没写线段树了,这个题一写完就WA了,后来发现,首先二分出来的区间先要判断下来,若区间不存在就不要进行插入了,还有就是pushdown,我一开始是计划每次只增加1
,要进行下一次懒惰标记的时候先把上一次 的懒惰标记down下去,结果在pushdown函数里面没判断儿子的标记,如果儿子标记了 也得先down儿子啊。结果这样超时了,还是不能这样,索性就每次懒惰标记直接++,最后把整个标记down下去就行
发现其实d数组完全没有必要的,这个值没有必要维护,只是一开始写的时候没计划的太清,就这样写了。其实没必要。
最后为了避免某个懒惰标记未被down下去,最后还用个incase函数再走一遍线段树,把值给更新一下。
相关文章推荐
- 第九届湖南省大学生程序设计竞赛H题 高桥,低桥(UVA 12663) BIT 树状数组
- UVA 12657/COJ 1329 HN第九届省赛 链表模拟
- UVA 12663 (线段树区间更新+二分查找,Q次区间更新后,求满足结果大于K的个数)
- 湖南第九届省赛 高桥和低桥
- TOJ 4602 高桥和低桥 -- 线段树 + 离散化
- BNU20410 UVA11992 线段树区间更新
- 第九届省赛-表达式求值(模拟)
- Uva 1400 "Ray, Pass me the dishes!" (线段树 区间合并)
- UVa 11235 Frequent values / RMQ or 线段树
- UVa 11992 Fast Matrix Operations / 线段树成段更新
- UVA 12436 Rip Van Winkle's Code(线段树区间更新)
- UVA 11983 Weird Advertisement --线段树求矩形问题
- UVa 1455 Kingdom 线段树 并查集
- UVA 11992 ——线段树(区间修改)
- UVa12532 - Interval Product(线段树)
- UVA1513--Movie collection--线段树
- ACM河南第九届省赛题
- UVA - 11402 Ahoy, Pirates! (线段树)
- UVa 11297 Census (二维线段树)
- UVA 11992 Fast Matrix Operations 线段树