HDU 3308 LCIS(线段树:单点更新,求最大连续子串)

HDU 3308 LCIS(线段树:单点更新,求最大连续子串)

http://acm.hdu.edu.cn/showproblem.php?pid=3308

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 the case number.
Each case starts with two integers n , m(0<n,m<=105).
The next line has n integers(0<=val<=105).
The next m lines each has an operation:
U A B(0<=A,n , 0<=B=105)
OR
Q A B(0<=A<=B< n).
 

Output
For each Q, output the answer.

分析:

      首先题目中找的递增序列是严格增加的,如果有7,7这样的序列不算递增.其次本题是单点更新,所以不用PushDown,只用PushUp就行.

       线段树维护信息:val(表节点的值,只有子节点有用,这个信息也可以不维护的),sub(最长上升子序列的长度),pre(最长上升前缀的长度),suf(最长上升后缀的长度).

由于一个区间[L,R]内的LCIS,可能在左半边,也可能跨越两边,也可能在右半边,所以对于任意区间的LCIS查询需要递归查询左右儿子的LCIS并且还要递归查询左儿子的最长上升后缀和右儿子的最长上升前缀.所以本题需要3个查询操作.

     PushUp(i,num)操作: 根据儿子节点的sub,pre,suf来更新父节点的sub,pre,suf.

       build(i,l,r)操作: 递归建树.最后需要PushUp更新父节点.

       update(q,v,i,l,r)操作: 递归到叶节点更新sub,pre,suf,然后在PushUp更新所有父节点的sub,pre,suf.

       query_pre(ql,qr,i,l,r)操作 :该操作的含义是返回区间[ql,qr]与[l,r]相交部分的最长前缀上升子序列长度. 即如果ql<=l<=qr的话,所指的序列就是以l开头的,否则如果l<ql, 所指的序列就是以ql开头的最长前缀序列. 那么以ql开头的如何求呢? 这个需要首先定位到那个[l,r]其中l正好==ql,然后求该节点的pre,看看pre是否==r-l+1,如果等于的话可以往后继续延伸,最多延伸到qr.

       query_suf(ql,qr,i,l,r)操作 : 该操作的含义是返回区间[ql,qr]与[l,r]相交部分的最长后缀上升子序列长度.如果r<=qr,那么直接返回suf[i]即可.否则如果r>qr的话, 需要定位到这样的j节点,它控制的区间[L,R]中的R正好==qr,然后求suf[j],如果suf[j]==R-L+1,那么就可以继续向前延伸.

       query(ql,qr,i,l,r)操作: 该操作的含义是返回区间[ql,qr]与[l,r]相交部分的最长上升子序列长度.如果[ql,qr]与[l,r]左儿子有相交就查左边的,如果右边有相交,还需要查右边的,如果都有相交,还需要综合考虑跨越了左儿子和右儿子的,即以左儿子的最长上升后缀,右儿子的最长上升前缀组成的.

AC代码:一次AC

<span style="font-size:18px;">#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,0,n-1
const int MAXN = 100000 + 100;
int val[MAXN];
int sub[MAXN * 4], pre[MAXN * 4], suf[MAXN * 4];

//统计下层信息到i节点,需要更新sub,pre,suf三信息
void PushUp(int i, int l, int r)
{
    int m = (l + r) / 2;

    sub[i] = max(sub[i * 2], sub[i * 2 + 1]); //更新sub
    if(val[m] < val[m + 1]) sub[i] = max(sub[i], suf[i * 2] + pre[i * 2 + 1]);

    pre[i] = pre[i * 2]; //更新pre
    if(pre[i] == m - l + 1 && val[m] < val[m + 1]) pre[i] += pre[i * 2 + 1];

    suf[i] = suf[i * 2 + 1]; //更新suf
    if(suf[i] == r - m && val[m] < val[m + 1]) suf[i] += suf[i * 2];
}

//建立线段树且更新所有节点的sub,suf,pre
void build(int i, int l, int r)
{
    if(l == r)
    {
        sub[i] = pre[i] = suf[i] = 1;
        return ;
    }
    int m = (r + l) / 2;
    build(lson);
    build(rson);
    PushUp(i, l, r);
}

//将q号元素的值置换为v
void update(int q, int v, int i, int l, int r)
{
    if(l == r)
    {
        val[l] = v;
        sub[i] = pre[i] = suf[i] = 1;
        return ;
    }
    int m = (r + l) / 2;
    if(q <= m) update(q, v, lson);
    else update(q, v, rson);
    PushUp(i, l, r);
}

//返回区间[ql,qr]与[l,r]最大目标前缀长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query_pre(int ql, int qr, int i, int l, int r)
{
    if(ql <= l && r <= qr)//[ql,qr]包含[l,r]
        return pre[i];
    int m = (r + l) / 2;
    if(qr <= m) return query_pre(ql, qr, lson);//[ql,qr]只与[l,m]相交
    if(m < ql) return query_pre(ql, qr, rson); //[ql,qr]只与[m+1,r]相交

    //[ql,qr]与[l,m]且[m+1,r]都相交
    int L = query_pre(ql, qr, lson); //先求左边的前缀长
    //特别注意这下面的max函数的意义,本函数取的是公共区间
    if(L == m - max(l,ql) + 1 && val[m] < val[m + 1]) L += query_pre(ql, qr, rson);
    return L;
}

//返回区间[ql,qr]与[l,r]最大目标后缀长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query_suf(int ql, int qr, int i, int l, int r)
{
    if(ql <= l && r <= qr)
        return suf[i];
    int m = (l + r) / 2;
    if(qr <= m) return query_suf(ql, qr, lson);
    if(m < ql) return query_suf(ql, qr, rson);
    int R = query_suf(ql, qr, rson);
    //特别注意这下面的min函数的意义,本函数取的是公共区间
    if(R == min(r,qr) - m && val[m] < val[m + 1]) R += query_suf(ql, qr, lson);
    return R;
}

//返回区间[ql,qr]与[l,r]最大目标子串长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query(int ql,int qr,int i,int l,int r)
{
    if(ql<=l && r<=qr)
    {
        return sub[i];
    }

    int m=(l+r)/2;
    if(qr<=m) return query(ql,qr,lson);
    else if(m<ql) return query(ql,qr,rson);
    int res=0;
    res=max(query(ql,qr,lson),query(ql,qr,rson));
    int pre=query_pre(ql,qr,rson);
    int suf=query_suf(ql,qr,lson);
    if(val[m]<val[m+1]) res=max(res,pre+suf);
    return res;
}
int main()
{
    int T, n, m;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++)
            scanf("%d", &val[i]);
        build(root);
        while(m--)
        {
            char str[10];
            int x, y;
            scanf("%s%d%d", str, &x, &y);
            if(str[0] == 'U')
                update(x, y, root);
            else if(str[0] == 'Q')
                printf("%d\n", query(x, y, root));
        }
    }
    return 0;
}

</span>

AC代码:不实现query_pre函数和query_suf函数的代码

<span style="font-size:18px;">#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,0,n-1
const int MAXN = 100000 + 100;
int val[MAXN];
int sub[MAXN * 4], pre[MAXN * 4], suf[MAXN * 4];

//统计下层信息到i节点,需要更新sub,pre,suf三信息
void PushUp(int i, int l, int r)
{
    int m = (l + r) / 2;

    sub[i] = max(sub[i * 2], sub[i * 2 + 1]); //更新sub
    if(val[m] < val[m + 1]) sub[i] = max(sub[i], suf[i * 2] + pre[i * 2 + 1]);

    pre[i] = pre[i * 2]; //更新pre
    if(pre[i] == m - l + 1 && val[m] < val[m + 1]) pre[i] += pre[i * 2 + 1];

    suf[i] = suf[i * 2 + 1]; //更新suf
    if(suf[i] == r - m && val[m] < val[m + 1]) suf[i] += suf[i * 2];
}

//建立线段树且更新所有节点的sub,suf,pre
void build(int i, int l, int r)
{
    if(l == r)
    {
        sub[i] = pre[i] = suf[i] = 1;
        return ;
    }
    int m = (r + l) / 2;
    build(lson);
    build(rson);
    PushUp(i, l, r);
}

//将q号元素的值置换为v
void update(int q, int v, int i, int l, int r)
{
    if(l == r)
    {
        val[l] = v;
        sub[i] = pre[i] = suf[i] = 1;
        return ;
    }
    int m = (r + l) / 2;
    if(q <= m) update(q, v, lson);
    else update(q, v, rson);
    PushUp(i, l, r);
}

//返回区间[ql,qr]与[l,r]最大目标子串长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query(int ql,int qr,int i,int l,int r)
{
    if(ql<=l && r<=qr)
    {
        return sub[i];
    }

    int m=(l+r)/2;
    if(qr<=m) return query(ql,qr,lson);
    else if(m<ql) return query(ql,qr,rson);

    int res=max(query(ql,qr,lson),query(ql,qr,rson));
    int prex=min(qr-m, pre[i*2+1]);//[m+1,qr]与[m+1,r]相交部分的最大前缀
    int sufx=min(m-ql+1, suf[i*2]);//[ql,m]与[l,m]的最大后缀
    if(val[m]<val[m+1]) res=max(res,prex+sufx);
    return res;
}
int main()
{
    int T, n, m;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++)
            scanf("%d", &val[i]);
        build(root);
        while(m--)
        {
            char str[10];
            int x, y;
            scanf("%s%d%d", str, &x, &y);
            if(str[0] == 'U')
                update(x, y, root);
            else if(str[0] == 'Q')
                printf("%d\n", query(x, y, root));
        }
    }
    return 0;
}
</span>


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值