HDU 3308 LCIS (经典区间合并)线段树

Posted 00isok

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 3308 LCIS (经典区间合并)线段树相关的知识,希望对你有一定的参考价值。

<题目链接>

题目大意:

给你一段序列,对其进行两种操作,一是修改某个序号的点的值;二是查询某个区间的LCIS(最长上升子序列)。

解题分析:

线段树区间合并的典型例题,用求某个区间的LCIS时,需要比较三个值,一是左区间的LCIS,二是右区间的LCIS,三是左右子区间合并的LCIS。最重要的是第三点如何实现,实现第三点需要维护一个最长后缀上升子序列和最长前缀上升子序列。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int siz=100005;
int a[siz],sum[siz<<2],lsum[siz<<2],rsum[siz<<2];

void pushup(int rt,int l,int r){          //区间合并,sum[rt]为该区间的最长上升子序列
    lsum[rt]=lsum[rt<<1];    //最长前缀
    rsum[rt]=rsum[rt<<1|1];  //最长后缀
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
    int m=(l+r)>>1;
    if(a[m]<a[m+1]){     //如果左右子区间的LCIS能够合并
        if(lsum[rt<<1]==(m-l+1))       //如果左子区间的最长前缀==左子区间长度
            lsum[rt]+=lsum[rt<<1|1];   //那么该节点的最长前缀除左子区间的最长前缀以外,还要加上右子区间的最长前缀
        if(rsum[rt<<1|1]==(r-m))       //如果右子区间的最长后缀==右子区间长度
            rsum[rt]+=rsum[rt<<1];     //那么该节点的最长后缀除右子区间的最长后缀外,还要加上左子区间的最长后缀
        sum[rt]=max(sum[rt],rsum[rt<<1]+lsum[rt<<1|1]);
    }                                           //区间合并
}
void build(int l,int r,int rt){
    if(l==r){          //注意这里的初始化
        sum[rt]=1;  
        lsum[rt]=rsum[rt]=1;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt,l,r);
}
void update(int loc,int val,int l,int r,int rt){         //单点更新
    if(l==r){
        a[l]=val;    
        return;
    }
    int m=(l+r)>>1;
    if(loc<=m)
        update(loc,val,l,m,rt<<1);
    else
        update(loc,val,m+1,r,rt<<1|1);
    pushup(rt,l,r);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R)
        return sum[rt];
    int m=(l+r)>>1;
    int ans=1;    //注意这里ans初始化为1

    /*-- 答案一共三种可能,在左区间或右区间或横跨两个区间 --*/
    if(L<=m)         //左、右区间的LCIS
        ans=max(ans,query(L,R,l,m,rt<<1));
    if(R>m)
        ans=max(ans,query(L,R,m+1,r,rt<<1|1));    

    if(L<=m&&R>=m&&a[m]<a[m+1])     //横跨左右两个子区间的情况
        ans=max(ans,min(m-L+1,rsum[rt<<1])+min(R-m,lsum[rt<<1|1]));     //rsum代表左区间最长后缀、lsum为右区间最长前缀
    return ans;
}

int main(){                                     //lsum为区间左端点开始长度
    char c;                                     //rsum为区间右端点开始长度
    int t,n,m;                            //sum为区间最长长度
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        build(1,n,1);
        while(m--){
            int u,v;
            cin>>c>>u>>v;
            if(c==‘Q‘){
                u++,v++;    //因为题目是从0开始编号
                printf("%d
",query(u,v,1,n,1));
            }
            else{
                u++;
                update(u,v,1,n,1);
            }
        }
    }
	return 0;
}

 

 

2018-09-11

以上是关于HDU 3308 LCIS (经典区间合并)线段树的主要内容,如果未能解决你的问题,请参考以下文章

hdu--3308 LCIS(线段树+区间合并)

hdu 3308 LCIS(线段树区间合并)

hdu 3308 LCIS(线段树)

[HDOJ3308]LCIS(线段树,区间合并)

HDU3308(LCIS) 线段树好题

[HDOJ3308]LCIS(线段树,区间合并,新的代码)