hdu 3308 LCIS(区间合并)

题目链接: hdu 3308 LCIS

题意:求给定区间最大上升序列的个数

#include<iostream>
#include<cstdio>
#define maxn 111111
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int lsum[maxn<<2],rsum[maxn<<2],msum[maxn<<2];
int ln[maxn<<2],rn[maxn<<2]; //ln存储区间最左边开始的上升系数,rn记录区间从右开始的上升序列数
void pushup(int rt,int m)
{
    lsum[rt]=lsum[rt<<1];
    rsum[rt]=rsum[rt<<1|1];
    msum[rt]=max(msum[rt<<1],msum[rt<<1|1]);
    ln[rt]=ln[rt<<1];  //父区间最左边的上升数就是就是子节点的数,右边同理
    rn[rt]=rn[rt<<1|1];
    if(rn[rt<<1]<ln[rt<<1|1] )  //当左儿子的最右端的值小于右儿子最左端的值,把这两段区间相连
    {
        if(lsum[rt]==(m-(m>>1)))    lsum[rt]+=lsum[rt<<1|1]; //左儿子的值等于区间个数,父区间的长度为左儿子区间加上右儿子的左区间
         if(rsum[rt]==(m>>1))         rsum[rt]+=rsum[rt<<1];  //右区间同理
        msum[rt]=max(msum[rt],rsum[rt<<1]+lsum[rt<<1|1]);//更改整个区间长度
    }
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%d",&ln[rt]);
        rn[rt]=ln[rt];
        lsum[rt]=rsum[rt]=msum[rt]=1;//叶子节点初始个数都为一
        return ;
    }
    int m =(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt,r-l+1);
    //printf("msum[%d %d] = %d\n",l,r,msum[rt]);
}
void update(int p,int c,int l,int r,int rt)
{
    if(l==r)
    {
        ln[rt]=rn[rt]=c;
        lsum[rt]=rsum[rt]=msum[rt]=1;
        return ;
    }
    int m =(l+r)>>1;
    if(p<=m) update(p,c,lson);
    else update(p,c,rson);
    pushup(rt,r-l+1);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)  return msum[rt];
    int m=(l+r)>>1;
    int ret = -1;
    if(L<=m)  ret=max(ret,query(L,R,lson));
    if(R>m)    ret=max(ret,query(L,R,rson));
    if(rn[rt<<1]<ln[rt<<1|1])    ret=max(ret,min(rsum[rt<<1],m-L+1)+min(lsum[rt<<1|1],R-m));//左右儿子相连时,长度应为相连总和,但要考虑到该区间的左右啦;两边可能与其他区间相连,所以长度可能会超过此区间的长度
   // printf("msum[%d %d] = %d , ret = %d\n",l,r,msum[rt],ret);
    return ret;
}
int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--)
        {
            char op;
            int a,b;
            scanf(" %c%d%d",&op,&a,&b);
            if(op=='Q')  printf("%d\n",query(a+1,b+1,1,n,1));
            else update(a+1,b,1,n,1);
        }

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值