杭电3308 LCIS(线段树区间合并)

线段树的学习真的是一件很不容易的事情.

漫长的道路望不到终点...繁杂的思路一时半会理不清T T



LCIS

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


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个数m个操作

U操作 把a位子上的数字改成b

Q操作 问区间(a,b)上最长连续递增子序列长度....(看起来好麻烦的样纸~)

这里先谈一下初始化的问题:

const int maxn=100000+5;
int msum[maxn<<2];//区间内最长连续递增子序列长度.
int lsum[maxn<<2];//区间从左边第一个数开始算起 到区间最后一个数的连续递增序列长度(比如(1,5)区间内数据:2 3 5 4 1)这个时候这个数组里边存的数据是2(2,3)
int rsum[maxn<<2];//右边的被(比如(1,5)区间内数据:5 4 1 2 3)这个时候数组里边存的数据是3(1,2,3)
int num[maxn];//存入数据的数组(并不是树哦~)
上边这部分的内容一定要理解透彻.


#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;//水一发头文件
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1//左右子树的快速调用写法~

无论做什么事情我们一定要有输入和建树:


        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)//这里输入强调 n个数的数据存入方式....跟树无关哦~.
        {
            scanf("%d",&num[i]);
        }


void build(int l,int r,int rt)//日常建树...
{
    if(l==r)
    {
        msum[rt]=lsum[rt]=rsum[rt]=1;//这里自行理解0.0
        return ;
    }
		int m = (l+r)>>1 ;
		build(lson) ;
		build(rson) ;
		pushup(l,r,rt) ;//主要还是通过建树的操作来印出来很关键的步骤.
}

这里就要浅谈一下pushup的操作,pushuup在线段树里边起到几个作用;1.维护树内数据2.向上更新该更新的数据

所以建树的同时 要不断的pushup pushup pushup------

这里我们知道lsum和rsum以及msum的含义.所以如果是向上维护数据的话 当前区间lsum就可以递推给上边的lsum既lsum[rt]=lsum[rt*2].

这个时候细心的小伙伴就要问了(假设现在区间是(1,5))如果现在的lsum的值应该是5但是你现在最大的值也就是个3 你这上坟烧报纸烧的略有些明显啊.

既然细心的小伙伴知道现在最大的值也就是个3 这个时候我们呢 就要找到3这个罚分点 然后判断是否能够区间合并 完成3->5的操作.

这里贴上pushup的操作代码加以详解:

void pushup(int l,int r,int rt)//((1,5),2)区间(l,r)节点rt //以下配合上边所述的例子来走的.
{
    lsum[rt]=lsum[rt*2];
    rsum[rt]=rsum[rt*2+1];
    msum[rt]=max(msum[rt*2],msum[rt*2+1]);//上边的语言描述尽我所能的解释了0.0
    int m=(l+r)/2;//罚分点.
    int len=r-l+1;//区间长度
    if(num[m]<num[m+1])//如果能有3->5的操作(也可以理解为能够区间合并)
    {
        if(lsum[rt]==len-(len/2))//首先我们要满足3的存在
        lsum[rt]+=lsum[rt*2+1];//这个时候我们就获得5的存在了
        if(rsum[rt]==len/2)//当然也有肯能是右边的3->5
        rsum[rt]+=rsum[rt*2];//我们不能只看左边而忘记了右边~
        msum[rt]=max(msum[rt],lsum[rt*2+1]+rsum[rt*2]);//msum用来存的数据是当前区间所求最大值.
    }

}

pushup的操作有了之后 ,我们建树的工程也就同时结束了.


接下来谈update(也就是U操作)

这部分还是比较好理解的 这里直接贴上代码和详解:

void update(int p,int l,int r,int rt)//找到p之前不断的pushup.//修改数据的操作在主函数中已经搞定了.
{
    if(l==r)
    return ;
    int m=(l+r)/2;
    if(p<=m)
    update(p,lson);
    else
    update(p,rson);
    pushup(l,r,rt);
}

最后再来说query(查询)操作.

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;
        if(R<=m)return query(L,R,lson);
        if(L>m)return query(L,R,rson);//左右区间的寻找.
        int a,b;
        a=query(L,R,lson);
        b=query(L,R,rson);
        int ans;
        ans=max(a,b);//ans首先取左右区间最大值.
        if(num[m]<num[m+1])//这里还有一个合并区间的问题.
        {
            int c;
            c=min(rsum[rt*2],m-L+1)+min(lsum[rt*2+1],R-m);//这里在纸上描描画画就懂咯~
            ans=max(c,ans);
        }
        return ans;
}


蓝后我们这里贴上完整的AC代码

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=100000+5;
int msum[maxn<<2];
int lsum[maxn<<2];
int rsum[maxn<<2];
int num[maxn];
void pushup(int l,int r,int rt)
{
    lsum[rt]=lsum[rt*2];
    rsum[rt]=rsum[rt*2+1];
    msum[rt]=max(msum[rt*2],msum[rt*2+1]);
    int m=(l+r)/2;
    int len=r-l+1;
    if(num[m]<num[m+1])
    {
        if(lsum[rt]==len-(len/2))
        lsum[rt]+=lsum[rt*2+1];
        if(rsum[rt]==len/2)
        rsum[rt]+=rsum[rt*2];
        msum[rt]=max(msum[rt],lsum[rt*2+1]+rsum[rt*2]);
    }

}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        msum[rt]=lsum[rt]=rsum[rt]=1;
        return ;
    }
		int m = (l+r)>>1 ;
		build(lson) ;
		build(rson) ;
		pushup(l,r,rt) ;
}
void update(int p,int l,int r,int rt)//找到p之前不断的pushup.
{
    if(l==r)
    return ;
    int m=(l+r)/2;
    if(p<=m)
    update(p,lson);
    else
    update(p,rson);
    pushup(l,r,rt);
}
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;
        if(R<=m)return query(L,R,lson);
        if(L>m)return query(L,R,rson);
        int a,b;
        a=query(L,R,lson);
        b=query(L,R,rson);
        int ans;
        ans=max(a,b);
        if(num[m]<num[m+1])
        {
            int c;
            c=min(rsum[rt*2],m-L+1)+min(lsum[rt*2+1],R-m);//防炸专用.
            ans=max(c,ans);
        }
        return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&num[i]);
        }
        build(1,n,1);
        while(m--)
        {
            char op[121];
            scanf("%s",op);
            int a,b;
            scanf("%d%d",&a,&b);
            if(op[0]=='U')
            {
                a++;
                num[a]=b;
                update(a,1,n,1);
            }
            else
            {
                a++;
                b++;
                printf("%d\n",query(a,b,1,n,1));
            }
        }
    }
}








你可能感兴趣的:(线段树,线段树,杭电,杭电,杭电,区间合并,杭电oj,杭电3308)