【线段树】Another LCIS

 题目链接: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;   
}   


 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值