码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-09-12 01:10:45      阅读:130      评论:0      收藏:0      [点我收藏+]

标签:cst   链接   hup   端点   16px   push   实现   合并   else   

<题目链接>

题目大意:

给你一段序列,对其进行两种操作,一是修改某个序号的点的值;二是查询某个区间的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\n",query(u,v,1,n,1));
            }
            else{
                u++;
                update(u,v,1,n,1);
            }
        }
    }
	return 0;
}

 

 

2018-09-11

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

标签:cst   链接   hup   端点   16px   push   实现   合并   else   

原文地址:https://www.cnblogs.com/00isok/p/9631802.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!