hdu3308

本文介绍了一种利用线段树解决最长连续上升子序列查询问题的方法。通过维护每个节点的最长连续上升序列信息,支持两种操作:更新指定位置的数值;查询指定区间内的最长连续上升子序列长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给你N个整数,有两种操作,
(1)”U A B”,表示把第A个数变成B。
(2)”Q A B”,表示查询区间[A,B]的最长连续上升序列。

分析:

明显是线段树的题。
lx表示区间左起最长连续上升序列的长度。
rx表示区间右起最长连续上升序列的长度。
mx表示区间最长连续上升序列的长度。
vl表示区间左端点的值。
vr表示区间右端点的值。

合并区间:

如果当前区间的左儿子的lx等于左儿子的区间长度,那么当前区间的lx还要加上右儿子的lx。维护当前区间的rx也是类似的,当前区间的mx除了在左右儿子的区间中的mx取一个最大值外,还要注意,如果左儿子的右端点小于右儿子的左端点那么当前区间的mx还有可能是左儿子的rx加上右儿子的lx。

查询:

如果查询的范围是在当前区间的左右儿子里,并且左儿子的右端点小于右儿子的左端点,那么不能直接取左儿子的rx加上右儿子的lx,因为有可能查询的范围的长度就已经小于左儿子的rx加上右儿子的lx,所以要加上一个判断。

代码:

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

using namespace std;
const int N=100100;
int n,m,ans,x,y,z,a[N];
struct edge{int l,r,mx,lx,rx,vl,vr,len;}g[4*N];
char s[100];

void pushup(int rt)
{
    g[rt].lx=g[2*rt].lx;
    g[rt].rx=g[2*rt+1].rx;
    g[rt].vl=g[2*rt].vl;
    g[rt].vr=g[2*rt+1].vr;
    g[rt].mx=max(g[2*rt].mx,g[2*rt+1].mx);

    if (g[2*rt].vr<g[2*rt+1].vl)
    {
        if (g[2*rt].lx==g[2*rt].len)
            g[rt].lx+=g[2*rt+1].lx;
        if (g[2*rt+1].rx==g[2*rt+1].len)
            g[rt].rx+=g[2*rt].rx;
        g[rt].mx=max(g[rt].mx,g[2*rt].rx+g[2*rt+1].lx);
    }

    g[rt].mx=max(g[rt].mx,max(g[rt].lx,g[rt].rx));
}

void build(int rt,int l,int r)
{
    g[rt].l=l;
    g[rt].r=r;
    g[rt].len=r-l+1;
    if (l==r)
    {
        g[rt].vl=g[rt].vr=a[l];
        g[rt].lx=g[rt].rx=g[rt].mx=1;
        return;
    }
    int mid=(l+r)/2;
    build(2*rt,l,mid);
    build(2*rt+1,mid+1,r);
    pushup(rt);
}

void update(int rt,int k,int val)
{
    int l=g[rt].l,r=g[rt].r;
    if (l==r)
    {
        g[rt].vl=g[rt].vr=val;
        return;
    }
    int mid=(l+r)/2;
    if (k<=mid)
        update(2*rt,k,val); else
        update(2*rt+1,k,val);
    pushup(rt);
}

int query(int rt,int ll,int rr)
{
    int l=g[rt].l,r=g[rt].r;
    if (ll<=l && r<=rr)
        return g[rt].mx;
    int mid=(l+r)/2;
    if (ll> mid) 
        return query(2*rt+1,ll,rr); else
    if (rr<=mid)
        return query(2*rt,ll,rr); else
    {
        int m1,m2,t1=0,t2=0;
        m1=query(2*rt,ll,rr);
        m2=query(2*rt+1,ll,rr);
        if (g[2*rt].vr<g[2*rt+1].vl)
        {
            t1=min(mid-ll+1,g[2*rt].rx);
            t2=min(rr-mid,g[2*rt+1].lx);
        }
        return max(max(m1,m2),t1+t2);
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
            scanf("%d",a+i);
        build(1,1,n);
        for (int i=1;i<=m;i++)
        {
            scanf("%s",s);
            if (s[0]=='Q')
            {
                scanf("%d%d",&x,&y);x++,y++;
                printf("%d\n",query(1,x,y));
            } else
            {
                scanf("%d%d",&x,&y);x++;
                update(1,x,y);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值