题目链接:http://acm.uestc.edu.cn/problem.php?pid=1425
思路:此题是一个线段树操作。要求的是对一个序列,进行成端增减一个值之后的最长连续上升序列长度。涉及到庞大量的成段更新,所以应该应用lazy思想以不至于超时,达到更新结点操作的时间均为o(lgn)。线段树中的结点需记录的信息详见代码注释。
类似这类成段更新的线段树的题目,都需要注意的两个地方:
1.在query时,要注意询问区间[l,r]和左右子区间的ls,rs的长度,记得取二者最小值,初学者易在此wa得不明不白。(代码相应处有注释)
2.lazy标记的意思是如果一个结点的lazy=1,则表示上一次更新到此节点。在把此节点更新信息向它的子节点下传完之后,自己本身要修改为0.所以啊,深入理解lazy是学习线段树的必修课。
我的代码中结点信息有点冗余,像神牛说的,其实lazy和inc只需一个即可,当inc非0时,即更新当前结点的左右子节点。不过我觉得多加一个好理解。。。
ps:废话一句,明天就出发去成都赛区了,本来说这个博客好好写数据结构和搜索和字符串的解题报告的,太懒了,写了一点就停了。区域赛前最后写一次,给pia们增点rp。。以后有机会,我会继续写的。。。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int N = 100000+10;
typedef long long LL;
inline char Rstr() { char str[10]; scanf("%s",str); return str[0];}
int n,q;
int a[N];
struct node
{
int left,right;
int ls,rs,len; //ls是包含左端点的最长连续上升序列长度,len是当前区间的最长连续上升序列长度
int lazy; //标记更新到该节点
int inc; //节点增量值
int lval,rval;//左右端点值,这个域很重要,当两个区间合并时,需要比较左子树的rval和右子树的lval来更新得到当前节点的len,lval,rval
};
node seg_tree[N<<2];
void dp(int rt) //更新结点各域
{
seg_tree[rt].ls = seg_tree[rt<<1].ls;
seg_tree[rt].rs = seg_tree[rt<<1|1].rs;
seg_tree[rt].len = max(seg_tree[rt<<1].len,seg_tree[rt<<1|1].len);
seg_tree[rt].lval = seg_tree[rt<<1].lval;
seg_tree[rt].rval = seg_tree[rt<<1|1].rval;
if(seg_tree[rt<<1].rval< seg_tree[rt<<1|1].lval)
seg_tree[rt].len = max(seg_tree[rt].len,seg_tree[rt<<1].rs+seg_tree[rt<<1|1].ls);
if(seg_tree[rt<<1].ls == seg_tree[rt<<1].right-seg_tree[rt<<1].left+1 && seg_tree[rt<<1].rval < seg_tree[rt<<1|1].lval)
seg_tree[rt].ls += seg_tree[rt<<1|1].ls;
if(seg_tree[rt<<1|1].rs==seg_tree[rt<<1|1].right-seg_tree[rt<<1|1].left+1 && seg_tree[rt<<1].rval < seg_tree[rt<<1|1].lval)
seg_tree[rt].rs += seg_tree[rt<<1].rs;
seg_tree[rt].len = max(max(seg_tree[rt].ls,seg_tree[rt].rs),seg_tree[rt].len);
}
void down(int rt,int inc)
{
seg_tree[rt].inc += inc;
seg_tree[rt].lazy = 1;
seg_tree[rt].lval += inc;
seg_tree[rt].rval += inc;
}
void creattree(int l,int r,int rt)
{
seg_tree[rt].left = l;
seg_tree[rt].right = r;
seg_tree[rt].lazy = 0;
seg_tree[rt].inc = 0;
if(l==r)
{
seg_tree[rt].len = seg_tree[rt].ls = seg_tree[rt].rs = 1;
seg_tree[rt].lval = seg_tree[rt].rval = a[l];
return ;
}
int mid = l+r>>1;
creattree(l,mid,rt<<1);
creattree(mid+1,r,rt<<1|1);
dp(rt);
}
void update(int l,int r,int val,int rt)
{
if(seg_tree[rt].left==l && seg_tree[rt].right==r)
{
down(rt,val);//update inc in function down()
return;
}
if(seg_tree[rt].lazy)
{
int tt = seg_tree[rt].inc;
down(rt<<1,tt);
down(rt<<1|1,tt);
seg_tree[rt].lazy = 0;
seg_tree[rt].inc = 0;
}
int mid = seg_tree[rt].left+seg_tree[rt].right>>1;
if(mid>=r) update(l,r,val,rt<<1);
else if(mid+1<=l) update(l,r,val,rt<<1|1);
else
{
update(l,mid,val,rt<<1);
update(mid+1,r,val,rt<<1|1);
}
dp(rt);
}
int query(int l,int r,int rt)
{
if(seg_tree[rt].left==l && seg_tree[rt].right==r)
return seg_tree[rt].len;
if(seg_tree[rt].lazy)
{
int tt = seg_tree[rt].inc;
down(rt<<1,tt);
down(rt<<1|1,tt);
seg_tree[rt].lazy = 0;
seg_tree[rt].inc = 0;
}
int mid = (seg_tree[rt].left+seg_tree[rt].right)>>1;
if(mid>=r) return query(l,r,rt<<1);
else if(mid+1<=l) return query(l,r,rt<<1|1);
else
{
int res = max(query(l,mid,rt<<1),query(mid+1,r,rt<<1|1));
if(seg_tree[rt<<1].rval<seg_tree[rt<<1|1].lval) //当左子树右端点值<右子树左端点值时
{
int len = min(seg_tree[rt<<1].right-l+1,seg_tree[rt<<1].rs) //初学者易出错的地方,当前欲询问区间和ls(rs)取个最小值
+ min(seg_tree[rt<<1|1].ls,r-seg_tree[rt<<1|1].left+1);
res = max(res,len);
}
return res;
}
}
int main()
{
int T;
scanf("%d",&T);
int p=1;
while(T--)
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
creattree(1,n,1);
printf("Case #%d:\n",p++);
while(q--)
{
char op;
int x,y;
op = Rstr();
if(op=='a')
{
int val;
scanf("%d%d%d",&x,&y,&val);
update(x,y,val,1);
}
else if(op=='q')
{
scanf("%d%d",&x,&y);
printf("%d\n",query(x,y,1));
}
}
}
return 0;
}