Codeforces 739C.Alyona and towers (线段树/非递归线段树)
来源:互联网 发布:淘宝优惠券群怎么建立 编辑:程序博客网 时间:2024/06/06 21:55
题目链接:http://codeforces.com/contest/739/problem/C
题意:给定30万个正整数,和30万个操作,每次操作后输出查询结果。
操作:每次把从L到R的数都加上D
查询:最宽的Hill的宽度。定义:先递增后递减的连续n个数称作宽度为n的Hill。
方法一:递归线段树(类似最长连续零的思路)1965ms/2000ms
这是最开始的思路,先定义Hill结构体,来存一个Hill的信息。
然后再定义Segment为线段树元素,其中包含3个Hill,从最左侧元素开始的最宽Hill,整个线段的最宽Hill,和最右侧线段的最宽Hill。
更新区间信息:
如果左子区间整个是一个Hill(代码中用pure表示),那么合并后的区间的左Hill=左子区间的左Hill 与 右子区间的左Hill合并。
这个合并是函数CLeft实现的,因为这个合并是基于左Hill的,计算出来的区间一定包含左Hill。
右子区间同理。合并后整个的最宽Hill = 左子区间最宽Hill ,右子区间最宽Hill 以及中间可能生成的新Hill中取最大值。
中间可能生成的新Hill是重载的Hill的加法。
总结:
这个算法不够简洁,Hill的加法居然写了3种。而且写到最后是卡时间过的。1965ms/2000ms
并且这是区间修改的线段树,带懒惰标记的。没办法写成非递归来加速。
方法二:差分+非递归线段树 389 ms/2000ms
看了官方题解和一些别人代码,发现别人的线段树是每个节点3个数的点修改线段树,而我写的是每个节点18个数的区间修改线段树。
首先是差分,假设原数组为A,则a[ i ] = A[ i + 1 ] - A[ i ] ,然后将正数记为1,负数记为-1.
差分的作用就是化区间修改为点修改,然后就可以愉快的用非递归优化了。
于是,题目转化为,从一个1,0,-1三个值组成的数列中,找到最长的(连续1后跟连续-1)的序列,以下简称序列。
注意这里的长度变化: Hill宽度 = 序列长度 + 1
先讲递归的:
每个节点存3个数,从左开始的最长序列的长度,整个区间最长序列的长度,从右开始的最长序列长度。
合并时,比较左区间的最右值和右区间的最左值。
当以下3个条件任何一个满足时,中间不会生成新序列,否则会生成新序列。
1.左区间最右值为0
2.右区间最左值为0
3.左区间最右值为-1 且 右区间最左值为1
不生成新序列时,合并很简单。
生成新序列时,要考虑左子区间是否是整个都是一个序列,才能判断如何计算合并区间的从左开始的序列。右子区间同理。
然后中间生成的新序列长度直接求和就行。
然后是非递归:
递归时只用记录3个长度,是因为区间的边界已经包含在线段树的函数的参数里了。
不用记录值,是因为可以通过下标直接访问a数组(这点在区间修改的线段树中是做不到的,所以方法一中必须存边界点对应的值)
而从递归到非递归,最简单的想法是,非递归线段树中,每个节点存它的左右边界,然后就跟递归的一样了。
不过我存的是,中间的界限的下标和该区间的长度。这样也可以,因为区间合并时,其实只需要求M=(L+R)/2.
于是我就直接记录M值,而不需要记录左右边界再来计算M,另一个用到左右边界的地方是判断是否整个区间都是一个序列,这时实际上是用的R-L+1.
于是我就直接记录区间长度Len。相当于省了一点点计算。
M和Len是在Build时计算,然后之后都不会改变了。
然后是前面提到的三个长度lv,mv,rv。
于是每个节点5个值。
要注意的一点是,递归时,a数组的大小只需要开到n-1(n为数字总数)就行了。
非递归时必须开到大于等于n+1(注意是加号)的最小的2的次方,因为在非递归线段树中直接访问了a数组,而非递归线段树是扩充了原数组数量的。
最后附上代码:
递归代码:
/*2016.12.4: Segment Tree AC 1965ms/2000ms*/#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#define maxn 300001#define LL long long#define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1using namespace std;struct Hill{ int Lid,Mid,Rid;//[L,R] LL Lv,Mv,Rv; bool valid; Hill():valid(false){} Hill(int Lid,int Mid,int Rid,LL Lv,LL Mv,LL Rv):Lid(Lid),Mid(Mid),Rid(Rid),Lv(Lv),Mv(Mv),Rv(Rv),valid(true){} int Len()const{return valid ? Rid - Lid + 1 : 0 ;} Hill operator +(const Hill B)const{ if(Rv == B.Lv) return Hill();//no new hill if(Rv < B.Lv){ if(Mid == Rid) return Hill(Lid,B.Mid,B.Rid,Lv,B.Mv,B.Rv); else return Hill(Rid,B.Mid,B.Rid,Rv,B.Mv,B.Rv); } else{ if(B.Mid == B.Lid) return Hill(Lid,Mid,B.Rid,Lv,Mv,B.Rv); else return Hill(Lid,Mid,B.Lid,Lv,Mv,B.Lv); } } bool operator < (const Hill B)const{return Len() < B.Len();}};Hill CLeft(const Hill A,const Hill B){//Combine A and B based on A if(!B.valid || B.Lv == A.Rv) return A; if(A.Rv > B.Lv){ if(B.Lid == B.Mid) return Hill(A.Lid,A.Mid,B.Rid,A.Lv,A.Mv,B.Rv); else return Hill(A.Lid,A.Mid,B.Lid,A.Lv,A.Mv,B.Lv); } else{ if(A.Mid == A.Rid) return Hill(A.Lid,B.Mid,B.Rid,A.Lv,B.Mv,B.Rv); else return A; }}Hill CRight(const Hill A,const Hill B){//Combine A and B based on B if(!A.valid || A.Rv == B.Lv) return B; if(B.Lv > A.Rv){ if(A.Rid == A.Mid) return Hill(A.Lid,B.Mid,B.Rid,A.Lv,B.Mv,B.Rv); else return Hill(A.Rid,B.Mid,B.Rid,A.Rv,B.Mv,B.Rv); } else{ if(B.Mid == B.Lid) return Hill(A.Lid,A.Mid,B.Rid,A.Lv,A.Mv,B.Rv); else return B; }}struct Segment{ Hill L,M,R; bool pure; Segment(){} Segment(Hill L,Hill M,Hill R,bool pure):L(L),M(M),R(R),pure(pure){} Segment operator +(const Segment B)const{ Segment Ans; Ans.L = pure ? CLeft(L,B.L) : L; Ans.R = B.pure ? CRight(R,B.R) : B.R; Hill CM = M < B.M ? B.M : M ; Ans.M = R + B.L; if(Ans.M < CM) Ans.M = CM; Ans.pure = Ans.M.Len() == Ans.R.Rid - Ans.L.Lid + 1; return Ans; } void Add(const LL D){ L.Lv += D; L.Mv += D; L.Rv += D; M.Lv += D; M.Mv += D; M.Rv += D; R.Lv += D; R.Mv += D; R.Rv += D; }};//Segment TreeSegment S[maxn<<2];LL Add[maxn<<2];int mm[maxn<<2];void PushUp(int rt){ S[rt] = S[rt << 1] + S[rt << 1 | 1];}void PushDown(int rt){ int ls = rt << 1 , rs = ls | 1; if(Add[rt]){ Add[ls] += Add[rt]; Add[rs] += Add[rt]; S[ls].Add(Add[rt]); S[rs].Add(Add[rt]); Add[rt] = 0; }}void Build(int l,int r,int rt){ if(l==r){ int v; scanf("%d",&v); Hill tempH(l,l,l,v,v,v); S[rt] = Segment(tempH,tempH,tempH,true); return; } int m = mm[rt] = (l + r) >> 1; Build(lson); Build(rson); PushUp(rt); Add[rt] = 0;}void Update(const int L,const int R,const LL D,const int l,const int r,const int rt){ if(L <= l && r <= R){ Add[rt] += D; S[rt].Add(D); return; } const int m = mm[rt]; PushDown(rt); if(L <= m) Update(L,R,D,lson); if(R > m) Update(L,R,D,rson); PushUp(rt);}int main(){ int n,m; while(~scanf("%d",&n)){ Build(1,n,1); scanf("%d",&m); for(int i = 0 ; i < m ; ++ i){ static int l,r,d; scanf("%d%d%d",&l,&r,&d); Update(l,r,d,1,n,1); printf("%d\n",S[1].M.Len()); } } return 0;}
非递归线段树:
/*2016.12.7: Non-recursive Segment Tree AC 389 ms / 2000 ms*/#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#define LL long longusing namespace std;struct Segment{ int M,Len; int lv,mv,rv;//How long the sequence is : left,max,right bool pure()const{return mv == Len;} Segment(){} Segment(int M,int Len,int lv,int mv,int rv):M(M),Len(Len),lv(lv),mv(mv),rv(rv){}};inline int sign(LL x) { return x ? x > 0 ? : -1 : 0;}LL a[524288];//a[i]=A[i+1]-A[i]; starting from 1//Segment Treeint N;Segment S[1048576];void PushUp(int rt){ //Triky part int ls = rt << 1 , rs = ls | 1; int M = S[rt].M; S[rt].mv = max(S[ls].mv,S[rs].mv); if(!a[M] || !a[M+1] || sign(a[M]) < sign(a[M+1])){ S[rt].lv = S[ls].lv; S[rt].rv = S[rs].rv; } else{ S[rt].lv = S[ls].pure() ? S[ls].lv + S[rs].lv : S[ls].lv; S[rt].rv = S[rs].pure() ? S[rs].rv + S[ls].rv : S[rs].rv; S[rt].mv = max(S[rt].mv,S[ls].rv+S[rs].lv); }}void Build(int n){//get values from sign(a[i]) where i <- [1..n] N = 1; while(N < n + 2) N <<= 1; //Fill Leaves for(int i = n + 1; i < N; ++i) a[i] = 0; for(int i = 0, j = N + i; i < N; ++i, ++j){ int x = !!sign(a[i]); S[j] = Segment(i,1,x,x,x); } //Fill the rest for(int i = N; --i;) { S[i].M = (S[i<<1].M + S[i<<1|1].M)>>1; S[i].Len = S[i<<1].Len + S[i<<1|1].Len; PushUp(i); }}void Update(int L,int D){ a[L] += D; if(sign(a[L]) == sign(a[L]-D)) return; else{ int x = !!sign(a[L]),s = N + L; S[s] = Segment(L,1,x,x,x); while(s>>=1) PushUp(s); }}int main(){ int n,m; while(~scanf("%d",&n)){ int pre,cur; scanf("%d",&pre); for(int i = 1; i < n; ++i){ scanf("%d",&cur); a[i] = cur - pre; pre = cur; } Build(n - 1); scanf("%d",&m); for(int i = 0 ; i < m ; ++ i){ static int l,r,d; scanf("%d%d%d",&l,&r,&d); if(l ^ 1) Update(l-1,d); if(r ^ n) Update(r,-d); printf("%d\n",S[1].mv+1); } } return 0;}
- Codeforces 739C.Alyona and towers (线段树/非递归线段树)
- 线段树 codeforces Alyona and towers 739C
- [Codeforces div1] Round 739C. Alyona and towers 线段树+差分数组
- Codeforces Round #381 (Div. 1) C. Alyona and towers(线段树)
- Codeforces Round #381 (Div. 2) E. Alyona and towers 差分+ 线段树并
- codeforces 739c Alyona and towers
- CodeForces 739C Alyona and towers
- 【CodeForces】- 739C Alyona and towers
- codeforce 777 C. Alyona and Spreadsheet (线段树)
- CF 739C Alyona and towers
- sgu263:Towers(线段树)
- Codeforces 12D.Ball (非递归线段树+离散化)
- Codeforces Round #381 (Div. 2) D. Alyona and a tree dfs+二分+线段树延迟操作、树形化线性
- codeforces 272C. Dima and Staircase(线段树)
- Codeforces 380C - Sereja and Brackets (线段树括号匹配)
- Codeforces 380C Sereja and Brackets(线段树)
- Codeforces 380C. Sereja and Brackets【线段树】
- CodeForces 296C Greg and Array (线段树)
- iput 按钮样式
- python 中NumPy和Pandas工具包中的函数使用笔记(方便自己查找)
- 集合A 两两一组 重新组合成集合B
- DroidPlugin代码分析(二) Hook机制
- 用/proc/stat计算cpu的占用率
- Codeforces 739C.Alyona and towers (线段树/非递归线段树)
- VSCode快捷键设置
- javascript事件——event对象
- 反转链表
- HTTPS原理
- Android 反编译初探 应用是如何被注入广告的
- iOS 相机工具类
- Java之--Stack栈
- 教你手机号码批量查询归属地的方法