HDU3308:LCIS(线段树区间合并)

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.

 

就是求区间最长上升字串,因为会更改值,所以不能dp,要用线段树区间合并

直接上代码,有详细注释

#include<iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#define LL long long
using namespace std;
const int MAX = 1e6 + 50;

struct date{
    int len; // 记录区间长度
    int ls, rs, ms; //记录左边开始的最长上升字串,右边开始的最长下降字串,以及该区间的最长上升字串的长度
    int ln, rn; // 记录该区间左右端点的值
} tree[MAX << 2];
int a[MAX];
void PushUp(int rt){
    int li = rt << 1;
    int ri = rt << 1 | 1;
    tree[rt].ls = tree[li].ls;
    tree[rt].rs = tree[ri].rs;
    tree[rt].ms = max(tree[li].ms, tree[ri].ms);
    tree[rt].ln = tree[li].ln;
    tree[rt].rn = tree[ri].rn;
    tree[rt].len = tree[li].len + tree[ri].len;

    if(tree[li].rn < tree[ri].ln){ //如果左区间的右端点的值小于右区间的左端点
        if(tree[li].len == tree[li].ls){ //如果左区间的以左开始的最长上升字串长的长度等于区间长度,则可以和右区间从左开始的最长上升子串合并
            tree[rt].ls += tree[ri].ls;
        }
        if(tree[ri].len == tree[ri].rs){
            tree[rt].rs += tree[li].rs;
        }
        tree[rt].ms = max(tree[rt].ms, tree[li].rs + tree[ri].ls); //判断ms的最大值
    }
}

void Build(int l, int r, int rt){
    if(l == r){
        tree[rt].ln = tree[rt].rn = a[l];
        tree[rt].ls = tree[rt].rs = tree[rt].ms = 1;
        tree[rt].len = 1;
        return ;
    }
    int m = (l + r) >> 1;
    Build(l, m, rt << 1);
    Build(m + 1, r, rt << 1 | 1);
    PushUp(rt);
}

int Le, Ri; //但点更新时Le为下标,Ri为值,  区间查询时Le, Ri分别为左右端点
void Update(int l, int r, int rt){
    if(l == r){
        tree[rt].ln = tree[rt].rn = Ri;
        return ;
    }

    int m = (l + r) >> 1;

    if(Le <= m){
        Update(l, m, rt << 1);
    }
    if(Le > m){
        Update(m + 1, r, rt << 1 | 1);
    }
    PushUp(rt);
}


int QueryRange(int l, int r, int rt){
    if(Le <= l && r <= Ri){
        return tree[rt].ms;
    }
    int m = (l + r) >> 1;
    if(Ri <= m){
        return QueryRange(l, m, rt << 1);
    }
    if(Le > m){
        return QueryRange(m + 1, r, rt << 1 | 1);
    }
    int t1 = QueryRange(l, m, rt << 1);
    int t2 = QueryRange(m + 1, r, rt << 1 | 1);
    int t3 = 0;
    int li = rt << 1;
    int ri = rt << 1 | 1;
    if(tree[li].rn < tree[ri].ln){ //如果左区间的右端点的值小于右区间的左端点的值
        t3 = min(m - Le + 1, tree[li].rs) + min(Ri - m, tree[ri].ls);
    }
    return max(max(t1, t2), t3);
}

int main(){
    int t;
    scanf("%d", &t);

    while(t--){
        int n, p;
        scanf("%d%d", &n, &p);
        for(int i = 1; i <= n; i++){
            scanf("%d", &a[i]);
        }
        Build(1, n, 1);
        while(p--){
            char c;
            scanf(" %c%d%d", &c, &Le, &Ri);
            Le++; //因为我们建树是从1开始的
            if(c == 'Q'){
                Ri++;
                int ans = QueryRange(1, n, 1);
                printf("%d\n", ans);
            } else{
                Update(1, n, 1);
            }
        }
    }
    return 0;

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值