HDOJ 3308 LCIS (线段树区间合并)

本文介绍了一种使用线段树解决求最长连续递增序列问题的方法,适用于10^5次操作场景。

题目链接

题意 : 求最长连续递增序列,10^5次操作。

思路 : 线段树区间合并实现。因为只是求连续的,所以只要记录每个区间左边起和右边结束还有最大的3个LCIS值,然后合并相应区间就行。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxn = 100005;
int max(int a,int b){return a > b ? a : b;}
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r

struct seg
{
    int l,r,lv,rv;
}rsum[maxn<<2],lsum[maxn<<2],sum[maxn<<2];
struct GOD
{
    seg ll, mm, rr;
};
int n, m;

int get(seg x)
{
    return x.r - x.l + 1;
}

void pushup(int rt, int M)
{
    lsum[rt] = lsum[rt<<1]; rsum[rt] = rsum[rt<<1|1];
    if (get(lsum[rt<<1]) == M - (M>>1) && lsum[rt<<1].rv < lsum[rt<<1|1].lv)
    {
        lsum[rt].r = lsum[rt<<1|1].r;
        lsum[rt].rv = lsum[rt<<1|1].rv;
    }
    if (get(rsum[rt<<1|1]) == (M>>1) && rsum[rt<<1|1].lv > rsum[rt<<1].rv)
    {
        rsum[rt].l = rsum[rt<<1].l;
        rsum[rt].lv = rsum[rt<<1].lv;
    }
    sum[rt] = (get(sum[rt<<1]) > get(sum[rt<<1|1]) ? sum[rt<<1] : sum[rt<<1|1]);
    if (rsum[rt<<1].rv < lsum[rt<<1|1].lv && get(rsum[rt<<1]) + get(lsum[rt<<1|1]) > get(sum[rt]))
    {
        sum[rt].l = rsum[rt<<1].l; sum[rt].lv = rsum[rt<<1].lv;
        sum[rt].r = lsum[rt<<1|1].r; sum[rt].rv = lsum[rt<<1|1].rv;
    }
}

void build(int rt, int l, int r)
{
    if (l == r)
    {
        int a;
        scanf("%d",&a);
        sum[rt].l = sum[rt].r = l;
        sum[rt].lv = sum[rt].rv = a;
        lsum[rt] = rsum[rt] = sum[rt];
        return ;
    }
    int mid = (l + r) >> 1;
    build(lson); build(rson);
    pushup(rt, r - l + 1);
}

void update(int rt, int l, int r, int p, int v)
{
    if (l == r)
    {
        sum[rt].lv = sum[rt].rv = v;
        lsum[rt] = rsum[rt] = sum[rt];
        return ;
    }
    int mid = (l + r) >> 1;
    if (mid >= p)update(lson, p, v);
    else update(rson, p, v);
    pushup(rt, r - l + 1);
}

GOD hebing(GOD a, GOD b)
{
    GOD res;
    res.ll = a.ll; res.rr = b.rr;
    if (a.rr.rv < b.ll.lv && get(a.rr) + get(b.ll) > max(get(a.mm), get(b.mm)))
    {
        res.mm.r = b.ll.r; res.mm.rv = b.ll.rv;
        res.mm.l = a.rr.l; res.mm.lv = a.rr.lv;
    }
    else
    {
        res.mm = (get(a.mm) > get(b.mm) ? a.mm : b.mm);
    }
    if (a.ll.rv < b.ll.lv && a.ll.r == b.ll.l - 1)
    {
        res.ll.r = b.ll.r;
        res.ll.rv = b.ll.rv;
    }
    if (a.rr.rv < b.rr.lv && b.rr.l == a.rr.r + 1)
    {
        res.rr.l = a.rr.l;
        res.rr.lv = a.rr.lv;
    }
    return res;
}

GOD query(int rt, int l, int r, int L, int R)
{
    GOD res;
    if (l >= L && r <= R)
    {
        res.ll = lsum[rt];
        res.rr = rsum[rt];
        res.mm = sum[rt];
        return res;
    }
    GOD gg;
    res.mm.l = gg.mm.l = -1;
    int mid = (l + r) >> 1;
    if (mid >= L)
    {
        res = query(lson, L, R);
    }
    if (R > mid) 
    {
        if (res.mm.l == -1)
            return query(rson, L, R);
        else
        {
            gg = query(rson, L, R);
            return hebing(res,gg);
        }
    }
    return res;
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        build(1, 1, n);
        while (m--)
        {
            char op[2];
            int a, b;
            scanf("%s %d%d",op, &a, &b);
            if (op[0] == 'U')
            {
                update(1, 1, n, a+1, b);
            }
            else
            {
                GOD ans = query(1, 1, n, a+1, b+1);
                printf("%d\n", get(ans.mm));
            }
            //printf("%s\n",op);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值