您的位置:首页 > 其它

HDU 3308 LCIS(最长连续上升子序列)(线段树区间合并)

2015-02-21 18:34 549 查看
题意:给你n个整数,有两种操作,U A B把第A个数变成B,Q A B查询区间[A,B]的最长连续上升序列。

思路:还是查询和更新操作,而且也是询问区间中满足条件的连续最长区间 ,所以是线段树区间合并类型的题,通法是开三棵线段树,一个记录此区间内的LCIS的最长长度,一个记录从左边第一个数开始的LCIS长度,另一个记录从右边最后一个数结尾的LCIS长度。然后试图找到父亲与儿子关系维护的递推关系式就好

本题中的递推关系是:

1. 左儿子最右边的值 < 右儿子最左边的值

lmx = (左儿子的lmx == 左儿子的len) ? 左儿子的len + 右儿子的lmx : 左儿子的lmx;

rmx = (右儿子的rmx == 右儿子的len) ? 右儿子的len + 左儿子的rmx : 右儿子的rmx;

mx = MAX(左儿子的rmx + 右儿子的lmx, 左儿子的mx, 右儿子的mx, 自身的lmx, 自身的rmx);

2. 左儿子最右边的值 >= 右儿子最左边的值

lmx = 左儿子的lmx;

rmx = 右儿子的rmx;

mx = MAX(左儿子的mx, 右儿子的mx);

在查询的时候,有个注意点,如果查询的范围是在当前区间的左右儿子里,并且左儿子的右端点小于右儿子的左端点,那么不能直接取左儿子的rmx加上右儿子的lmx,因为有可能查询的范围的长度就已经小于左儿子的rmx加上右儿子的lmx,所以要加上一个判断。

学习了大神http://blog.csdn.net/shiqi_614/ 的代码风格。

用结构体封装了整个线段树的所有函数

用结构体封装了线段树每个结点里面要记录的内容和一些常用的函数(算中点,算区间长度)

这样线段树就格式化了...233..而且添加要记录的内容也变得清晰方便,比如这题可以在结点里面加个lval和rval记录区间左右端点对应的值,因题而异,清晰灵活。

//280MS 8676K
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int M=1e5+5;
#define LL(x) (x<<1)  //左儿子序号
#define RR(x) (x<<1|1)  //右儿子序号
#define MID(a,b) (a+((b-a)>>1) ) //小技巧

int num[M],n,m;
struct node  //线段树的结点内容
{
    int l,r;
    int lval,rval;  //区间左右端点对应的num[i]
    int lmx,rmx,mx; //区间合并的三巨头(基础)
    int mid(){return MID(l,r);}
    int len(){return r-l+1;}
};

struct Segtree  //封装线段树的全部函数
{
    node tree[M<<2];
    void up(int rt)
    {
        tree[rt].lval=tree[LL(rt)].lval;
        tree[rt].rval=tree[RR(rt)].rval;
        tree[rt].lmx= tree[LL(rt)].lmx;
        tree[rt].rmx= tree[RR(rt)].rmx;
        tree[rt].mx=-1;   //别丢了,多组数据要初始化,开始就wa了一次
        if(tree[LL(rt)].rval<tree[RR(rt)].lval){

            if(tree[rt].lmx==tree[LL(rt)].len())
                tree[rt].lmx+=tree[RR(rt)].lmx;

            if(tree[rt].rmx==tree[RR(rt)].len())
                tree[rt].rmx+=tree[LL(rt)].rmx;

            tree[rt].mx = tree[LL(rt)].rmx + tree[RR(rt)].lmx;

        }
        tree[rt].mx=max(tree[rt].mx,max( max(tree[LL(rt)].mx,tree[RR(rt)].mx),max(tree[rt].lmx,tree[rt].rmx)  ));
        //这步是递推关系式要考虑全多种情况
    }

    void build(int l,int r,int rt)
    {
        tree[rt].l=l;
        tree[rt].r=r;
        if(l==r){
            tree[rt].lval=tree[rt].rval=num[l];
            tree[rt].lmx=tree[rt].rmx=tree[rt].mx=1;
            return ;
        }
        int m= tree[rt].mid();
        build(l,m,LL(rt));
        build(m+1,r,RR(rt));
        up(rt);
    }

    void update(int pos,int rt,int val)
    {
        if(tree[rt].l==tree[rt].r){
            tree[rt].lval=tree[rt].rval=val;
            tree[rt].lmx=tree[rt].rmx=tree[rt].mx=1;
            return ;
        }

        int m=tree[rt].mid();
        if(pos<=m) update(pos,LL(rt),val);
        else update(pos,RR(rt),val);
        up(rt);
    }

    int query(int st,int ed,int rt)
    {
        if(st<=tree[rt].l&&tree[rt].r<=ed){
            return tree[rt].mx;
        }

        int m = tree[rt].mid();
        if(ed<=m) return query(st,ed,LL(rt));
        if(st>m) return query(st,ed,RR(rt));

        int mx1=query(st,ed,LL(rt));
        int mx2=query(st,ed,RR(rt));
        if(tree[LL(rt)].rval<tree[RR(rt)].lval ){
            int m=tree[rt].mid();
            int t1=min(tree[LL(rt)].rmx,m-st+1 );
            int t2=min(tree[RR(rt)].lmx,ed-m);
            return max(max(mx1,mx2),t1+t2); //查询也要考虑多种情况防止遗漏

        }
        else return max(mx1,mx2);

    }

}seg;

int main()
{

    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) scanf("%d",&num[i]);
        seg.build(0,n-1,1);
        while(m--){
            char op[5];
            int a,b;
            scanf("%s%d%d",op,&a,&b);
            if(op[0]=='U'){
                seg.update(a,1,b);
            }
            else printf("%d\n",seg.query(a,b,1));
        }

    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: