题目链接: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
#include
#include
#include
#define maxn 300001
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using 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 Tree
Segment 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
#include
#include
#include
#define LL long long
using 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 Tree
int 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;
}