5W次单点修改,求最长的连续上升子序列 HDU 3308

题目大意:给你n个数,m个操作。

有两种操作:

1.U x y 将数组第x位变为y  

2. Q x y 问数组第x位到第y位连续最长子序列的长度。

对于每次询问,输出连续最长子序列的长度

思路:用线段树维护上升序列,每个端点维护:左边连续递增的len,右边连续递增的len,中间连续递增的len,左边val,右边val,和目前的len。然后不断更新即可。

注意:如果左区间的最右边的值小于右区间最左边的值,则有一个待定答案是左儿子的右区间+右儿子的左区间

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha
")
/*
①记录本身内部的序列长度
②要将区间合并的时候,rightson的left区间和leftson的right区间判断条件以后再合并。
如果合并区间是对于两端的值
*/
const int maxn = 1e5 + 5;
struct Tree{
    int leftlen, rightlen, midlen, leftval, rightval, len;
}tree[maxn << 2];
int n, m;

inline void pushup(int o){
    int lb = o << 1, rb = o << 1 | 1;
    int lbval = tree[lb].rightval, rbval = tree[rb].leftval;
    bool flag = false;
    tree[o].midlen = max(tree[lb].rightlen, max(tree[rb].leftlen, max(tree[lb].midlen, tree[rb].midlen)));
    if (lbval < rbval) {
        flag = true;
        tree[o].midlen = max(tree[lb].rightlen + tree[rb].leftlen, tree[o].midlen);
    }

    tree[o].leftlen = tree[lb].leftlen;
    if (tree[lb].rightlen == tree[lb].len && flag) tree[o].leftlen = tree[o].midlen;

    tree[o].rightlen = tree[rb].rightlen;
    if (tree[rb].leftlen == tree[rb].len && flag) tree[o].rightlen = tree[o].midlen;
    tree[o].leftval = tree[lb].leftval, tree[o].rightval = tree[rb].rightval;
}

void buildtree(int l, int r, int o){
    tree[o].len = r - l + 1;
    if (l == r){
        int val; scanf("%d", &val);
        tree[o].leftlen = tree[o].rightlen = tree[o].midlen = 1;
        tree[o].leftval = tree[o].rightval = val;
        return ;
    }
    int mid = (l + r) / 2;
    if (l <= mid) buildtree(l, mid, o << 1);
    if (r > mid) buildtree(mid + 1, r, o << 1 | 1);
    pushup(o);
    //printf("l = %d r = %d leftlen = %d rightlen = %d midlen = %d
", l, r, tree[o].leftlen, tree[o].rightlen, tree[o].midlen);
}

void update(int pos, int val, int l, int r, int o){
    if (pos == l && pos == r){
        tree[o].leftval = tree[o].rightval = val; return ;
    }
    int mid = (l + r) / 2;
    if (pos <= mid) update(pos, val, l, mid, o << 1);
    if (pos > mid) update(pos, val, mid + 1, r, o << 1 | 1);
    pushup(o);
}

int query(int ql, int qr, int l, int r, int o){
    int ans = 1;
    if (ql <= l && qr >= r){
        ans = max(ans, max(tree[o].leftlen, max(tree[o].rightlen, tree[o].midlen)));
        return ans;
    }
    int mid = (l + r) / 2;
    int t1 = -1, t2 = -1;
    if (mid >= ql){
        t1 = query(ql, qr, l, mid, o << 1);
    }
    if (mid < qr){
        t2 = query(ql, qr, mid + 1, r, o << 1 | 1);
    }
    ans = max(ans, max(t1, t2));
    ///如果左区间的最右边的值小于右区间最左边的值,则有一个待定答案是左儿子的右区间+右儿子的左区间
    if (tree[o << 1].rightval < tree[o << 1 | 1].leftval && t1 > 0 && t2 > 0){
        t1 = min(tree[o << 1].rightlen, mid - ql + 1) + min(tree[o << 1 | 1].leftlen, qr - mid);
        ans = max(ans, t1);
    }
    return ans;
}

int main(){
    int t; cin >> t;
    while (t--){
        scanf("%d%d", &n, &m);
        buildtree(1, n, 1);
        for (int i = 1; i <= m; i++){
            char ch[2]; int a, b;
            scanf("%s%d%d", ch, &a, &b);
            if (ch[0] == 'U'){
                update(a + 1, b, 1, n, 1);
            }
            else {
                printf("%d
", query(a + 1, b + 1, 1, n, 1));
            }
        }
    }
    return 0;
}
View Code

关键:

感觉和以前的一题CF很像,忘了是哪里的了......

原文地址:https://www.cnblogs.com/heimao5027/p/6358782.html