hdu_3308 LCIS 区间合并 求最长递增子序列长度

Time Limit: 2000MS Memory Limit: 32768KB 64bit IO Format: %I64d & %I64u

 Status

Description

Given n integers. 
You have two operations: 
U A B: replace the Ath number by B. (index counting from 0) 
Q A B: output the length of the longest consecutive increasing subsequence (LCIS) in [a, b]. 
 

Input

T in the first line, indicating the case number. 
Each case starts with two integers n , m(0<n,m<=10  5). 
The next line has n integers(0<=val<=10  5). 
The next m lines each has an operation: 
U A B(0<=A,n , 0<=B=10  5
OR 
Q A B(0<=A<=B< n). 
 

Output

For each Q, output the answer.
 

Sample Input

        
        
1 10 10 7 7 3 3 5 9 9 8 1 8 Q 6 6 U 3 4 Q 0 1 Q 0 5 Q 4 7 Q 3 5 Q 0 2 Q 4 6 U 6 10 Q 0 9
 

Sample Output

        
        
1 1 4 2 3 1 2 5
 

Source

HDOJ Monthly Contest � 2010.02.06

 Status

 运用线段树求最长递增子序列长度,可以想象,线段树一个节点存的主要信息:左起最大连续长度,右起最大连续长度,中间最大连续长度。。。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define mem0(a) memset(a,0,sizeof(a))
const int  maxn = 100000+10;
struct node {
    int l,r;
    int lsum,rsum,msum;
}a[maxn<<2];
int b[maxn];
inline void pushup(int cur){
    a[cur].msum = max(a[cur<<1].msum,a[cur<<1|1].msum);
    a[cur].lsum = a[cur<<1].lsum ;
    a[cur].rsum = a[cur<<1|1].rsum ;
    if(b[a[cur<<1].r] < b[a[cur<<1|1].l]){
//如果左子树的右边界值小于右子树的左边界值,要合并左子树的右边界和右子树的左边界进行计算
        a[cur].msum = max(a[cur].msum,a[cur<<1].rsum+a[cur<<1|1].lsum);
                                //左子树右边最大连续长度+右儿子左边最大连续长度
        //这两个条件都是建立在左子树右边值小于右子树左边值的基础上,否则无需更新
        if(a[cur<<1].lsum == a[cur<<1].r - a[cur<<1].l +1)//该节点左子树左起最大连续长度等于区间长度
            a[cur].lsum += a[cur<<1|1].lsum;//该节点左起最大连续长度为左子树长度+右子树左起最大长度
        if(a[cur<<1|1].rsum == a[cur<<1|1].r - a[cur<<1|1].l +1)//节点右子树右起最大长度等于区间长度
            a[cur].rsum += a[cur<<1].rsum ;//节点右起最大长度为右子树长度+左子树右起最大长度
    }
}
void build(int l,int r,int cur){
    a[cur].l = l ;
    a[cur].r = r ;
    if(l == r){
        scanf("%d",&b[l]);
        a[cur].lsum = a[cur].rsum = a[cur].msum = 1;
        return ;
    }
    int mid = ( l + r)>>1;
    build(l,mid,cur<<1);
    build(mid+1,r,cur<<1|1);
    pushup(cur);
}
void update(int x,int v, int cur){
    if(a[cur].l == a[cur].r && x == a[cur].l){
        b[x] = v;
        return ;
    }
    int mid = (a[cur].l + a[cur].r )>>1;
    if(x <= mid)
        update(x,v,cur<<1);
    else
        update(x,v,cur<<1|1);
    pushup(cur);

}
int query(int l,int r,int cur){
    if(l <= a[cur].l && r >= a[cur].r)
        return a[cur].msum ;
    int mid = (a[cur].l+ a[cur].r)>>1;
    if(r <= mid)
        return query(l,r,cur<<1);//最大lcis在左子树
    else if( l > mid )
        return query(l,r,cur<<1|1);//在右子树
    else {//最大lcis在中间,跨越两个子树
        int ans = 0 ;
        if(b[a[cur<<1].r] < b[a[cur<<1|1].l]){//当左子树右端点小于右子树左端点时
            int ll = a[cur<<1].rsum ,rr = a[cur<<1|1].lsum;
            if( ll > mid - l +1)//防止rsum/lsum长度大于当前区间长度
             //之前更新的lsum/rsum可能接到另一边,长度可能大于当前区间长度
               ll  = mid - l + 1;
            if(rr > r - (mid+1) +1)
                rr = r - (mid+1) +1;
            ans = max(ans,ll + rr);//求中间最长lcis长度
        } 
        ans = max(ans,query(l,mid,cur<<1));//与左边最大LCIS长度
        ans = max(ans,query(mid+1,r,cur<<1|1));//再与右边最大LCIS长度
      //这种情况是查询区间正好横跨左右子树,但左右子树相邻点并不递增
        //比如左子树 5 6 7 ,右子树为 1 3 6,得分开比较
      return ans ;
    }
}
int main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        build(1,n,1);
        char s[3];
//        for(int i = 1 ; i <= n ; i++)
//            printf("%d ",b[i]);
        while(m--){
            int a,b;
            scanf("%s%d%d",s,&a,&b);
            if(s[0]=='Q'){//题上编号是从0开始,为方便计算,统一加1不影响结果
                printf("%d\n",query(a+1,b+1,1));
            }
            else if(s[0]=='U'){
                update(a+1,b,1);
            }
        }
    }
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值