hdoj 3308 LCIS 【线段树单点更新 + 区间合并】【求解最长递增序列 的长度】



LCIS

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5606    Accepted Submission(s): 2447


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<=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
 



题意:给你n个数组成的序列(序列中编号从0到n-1),有q次操作。

操作1——Q a  b,让你输出区间[a, b]里面最长的连续递增序列的长度;

操作2——U a  b,修改序列第a个数为b。


线段树单点更新 + 区间合并,重在PushUp函数的操作 和 查询操作。


AC代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100000+10
#define lson o<<1, l, mid
#define rson o<<1|1, mid+1, r
#define ll o<<1
#define rr o<<1|1
using namespace std;
struct Tree
{
    int l, r, len;//区间左右端点 和 长度
    int lv, rv;//区间左端点的值  右端点的值
    int lsum, rsum;//以区间左端点为起点的最长递增序列长度 以区间右端点为终点的最长递增序列长度
    int sum;//区间 最长递增序列长度
};
Tree tree[MAXN<<2];
void PushUp(int o)
{
    tree[o].lv = tree[ll].lv;//左子树的左端点值
    tree[o].rv = tree[rr].rv;//右子树的右端点值
    tree[o].lsum = tree[ll].lsum;
    tree[o].rsum = tree[rr].rsum;
    tree[o].sum = max(tree[ll].sum, tree[rr].sum);
    if(tree[ll].rv < tree[rr].lv)
    {
        if(tree[ll].lsum == tree[ll].len)//左半区间完全容纳最长递增序列 向右延伸
            tree[o].lsum += tree[rr].lsum;
        if(tree[rr].rsum == tree[rr].len)//右半区间完全容纳最长递增序列 向左延伸
            tree[o].rsum += tree[ll].rsum;
        //更新  左子树的右端点连续长度 + 右子树的左端点的连续长度
        tree[o].sum = max(tree[o].sum, tree[ll].rsum + tree[rr].lsum);//更新最值
    }
}
int a;
void build(int o, int l, int r)//建树
{
    tree[o].l = l, tree[o].r = r;
    tree[o].len = r -l + 1;
    if(l == r)
    {
        scanf("%d", &a);
        tree[o].lv = tree[o].rv = a;
        tree[o].lsum = tree[o].rsum = tree[o].sum = 1;
        return ;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUp(o);
}
int query(int o, int L, int R)
{
    if(L <= tree[o].l && R >= tree[o].r)
        return tree[o].sum;
    int mid = (tree[o].l + tree[o].r) >> 1;
    int ans = 0;
    if(L <= mid)//更新左子树
        ans = max(ans, query(ll, L, R));
    if(R > mid)//更新右子树
        ans = max(ans, query(rr, L, R));
    if(tree[ll].rv < tree[rr].lv)//更新最大值  注意要取最小段 不能超过要查询的区间
        ans = max(ans, min(mid-L+1, tree[ll].rsum) + min(R-mid, tree[rr].lsum));
    return ans;
}
void update(int o, int pos, int val)
{
    if(tree[o].l == tree[o].r)//修改
    {
        tree[o].lv = tree[o].rv = val;
        return ;
    }
    int mid = (tree[o].l + tree[o].r) >> 1;
    if(pos <= mid)
        update(ll, pos, val);
    else
        update(rr, pos, val);
    PushUp(o);//维护 上传
}
int main()
{
    int t;
    int n, q;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &q);
        build(1, 0, n-1);//建树
        char str[5];
        int a, b;
        while(q--)
        {
            scanf("%s%d%d", str, &a, &b);
            if(str[0] == 'Q')
                printf("%d\n", query(1, a, b));
            else
                update(1, a, b);
        }
    }
    return 0;
}



你可能感兴趣的:(hdoj 3308 LCIS 【线段树单点更新 + 区间合并】【求解最长递增序列 的长度】)