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\n",query(u,v,1,n,1));
            }
            else{
                u++;
                update(u,v,1,n,1);
            }
        }
    }
	return 0;
}

2018-09-11

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

时间: 2024-08-30 01:13:21

HDU 3308 LCIS (经典区间合并)【线段树】的相关文章

HDU 3308 LCIS(区间合并 + 单点更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题意:给定n个数.2种操作. 更新第a个数为b. 查询区间[a,b]的最长连续上升子序列. 思路:裸的区间合并.每个结点存 从区间左端点开始的最长连续上升子序列的长度lm. 以区间右端点结束的最长连续上升子序列的长度rm. 区间的最长连续上升子序列的长度mx. 区间左端点的数值la. 区间右端点的数值ra. 代码: #include <iostream> #include <stdio

【区间合并线段树】BZOJ1593-安排住宿

[题目大意] 查询最左端的连续长度区间:或批量修改一些区间.[思路] 区间合并线段树……复习一下.POJ上有一样的题目,我居然还借用了别人的权限号去做BZOJ,简直愚昧到没朋友[笑cry] 处理方法以前的博文里有,这里有不赘述了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 using

【区间合并线段树】HDU 4509 模板

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int MAXN=100000+50; const int rt=0; vector<

BZOJ 2243:染色(树链剖分+区间合并线段树)

[SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”.请你写一个程序依次完成这m个操作.Input第一行包含2个整数n和m,分别表示节点数和操作数:第二行包含n个正整数表示n个节点的初始颜色下面 行每行包含两个整数x和y,表示x和y之间有一条无向边.下面 行每行描述一个操作:“C

HDU 3308 LCIS (线段树&#183;单点更新&#183;区间合并)

题意  给你一个数组  有更新值和查询两种操作  对于每次查询  输出对应区间的最长连续递增子序列的长度 基础的线段树区间合并  线段树维护三个值  对应区间的LCIS长度(lcis)  对应区间以左端点为起点的LCIS长度(lle)  对应区间以右端点为终点的LCIS长度(lri)  然后用val存储数组对应位置的值  当val[mid + 1] > val[mid] 的时候就要进行区间合并操作了 #include <cstdio> #include <algorithm>

HDU 3308 LCIS (端点更新+区间合并)

刚刚做了两道LCIS,碰到这道线段树,脑抽了似的写 线段树+dp(LCIS),贡献一发TLE. 才想到要区间合并,query函数写了好久.下面有详细注释,参见代码吧~~欢迎点赞,欢迎卖萌~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 #include<cstdio> #inc

hdu 3308 LCIS(线段树)

pid=3308" target="_blank" style="">题目链接:hdu 3308 LCIS 题目大意:给定一个序列,两种操作: Q l r:查询区间l,r中的最长连续递增序列长度 U p x:将位置p上的数改成x 解题思路:线段树上的区间合并,这是在左右子树合并的时候要推断一下是否满足递增就可以. #include <cstdio> #include <cstring> #include <algorit

HDU 3308 LCIS(线段树)

Problem 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

hdu 1556:Color the ball(线段树,区间更新,经典题)

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7941    Accepted Submission(s): 4070 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌电