hdu3308LCIS(线段树,点更新,段查寻,查寻时一定要注意跨越时如何计算)

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
题意:输入Q,则查寻范围[A,B]内的最长升序,输入U,则更新点A的值变成B。
解题:记录第个节点的最左端的升序长度,最右端的升序的长度和在这个范围内的最长升序。而父节点的则是由左右节点产生。
1. 左儿子最右边的值 < 右儿子最左边的值



    lMax = (左儿子的lMax == 左儿子的len) ? 左儿子的len + 右儿子的lMax : 左儿子的lMax;

    rMax = (右儿子的rMax == 右儿子的len) ? 右儿子的len + 左儿子的rMax : 右儿子的rMax;

    Max  = MAX(左儿子的rMax + 右儿子的lMax, 左儿子的Max, 右儿子的Max, lMax, rMax);



2. 左儿子最右边的值 >= 右儿子最左边的值

    lMax = 左儿子的lMax;

    rMax = 右儿子的rMax;

    Max  = MAX(左儿子的Max, 右儿子的Max);
注意:在查寻时,当跨越左右子树时则一这要注意这个,当 左节点的最右边的值< 右节点的最左边的值时,那么有可能最长升序在这时最大,并且不能超过这个范围。
#include<stdio.h>

#define N 500010

struct node

{

    int Llcis,Rlcis,lcis;//最左连续升序长度,最右连续升序长度,这个范围的最长连续升序长度

}tree[8*N];

int num[N+5];

int max(int a,int b){ return a>b?a:b;}

void chang_tree(int l,int r,int k)//根据第K个节点的左右节点,计算第K个节点的最左,最右和最长 的升序长度

{

    int m=(l+r)/2;

     node ltree=tree[k*2],rtree=tree[k*2+1];

    if(num[m]>=num[m+1])//当左节点的最右边的值不小右节点的最左边的值时

    {

        tree[k].Llcis=ltree.Llcis;

        tree[k].Rlcis=rtree.Rlcis;

        tree[k].lcis=max(ltree.lcis,rtree.lcis);

    }

    else

    {

        if(m-l+1==ltree.Llcis) tree[k].Llcis=ltree.Llcis+rtree.Llcis;

           else  tree[k].Llcis=ltree.Llcis;

        if(r-m==rtree.Rlcis) tree[k].Rlcis=rtree.Rlcis+ltree.Rlcis;

            else  tree[k].Rlcis=rtree.Rlcis;

        tree[k].lcis=max(ltree.lcis,ltree.Rlcis+rtree.Llcis);

        tree[k].lcis=max(tree[k].lcis,rtree.lcis);

        tree[k].lcis=max(tree[k].lcis,tree[k].Llcis);

        tree[k].lcis=max(tree[k].lcis,tree[k].Rlcis);

    }

}

void build(int l,int r,int k)

{

    int m=(l+r)/2;

    if(l==r){

        tree[k].Llcis=1;

        tree[k].lcis=1;

        tree[k].Rlcis=1;

        return ;

    }

    build(l,m,k*2);

    build(m+1,r,k*2+1);

    chang_tree(l,r,k);

}

void updata(int l,int r,int k,int Q,int ans)

{

    int m=(l+r)/2;

    if(l==Q&&Q==r)

    {num[Q]=ans; return ;}

    if(Q<=m) updata(l,m,k*2,Q,ans);

    if(Q>m) updata(m+1,r,k*2+1,Q,ans);

    chang_tree(l,r,k);

}

int maxlen;

void find(int l,int r,int k,int L,int R)

{

    int m=(l+r)/2;

    if(L<=l&&r<=R){

           maxlen=max(tree[k].lcis,maxlen);

           return ;

    }

    if(R<=m) find(l,m,k*2,L,R);

    else if(L>m) find(m+1,r,k*2+1,L,R);

    else{

        find(l,m,k*2,L,R);

        find(m+1,r,k*2+1,L,R);

    //-----当查寻夸越左右子树时,左子树的最右端点的值小于右子树的最左端点的值------

        if(num[m]<num[m+1])

            if(L<=m-tree[2*k].Rlcis+1&&m+tree[k*2+1].Llcis<=R)//注意这个条件

           maxlen=max(tree[k*2].Rlcis+tree[k*2+1].Llcis,maxlen);

            else if(L<=m-tree[2*k].Rlcis+1&&m+tree[k*2+1].Llcis>R)//注意这个条件

              maxlen=max(tree[k*2].Rlcis+R-m,maxlen);

            else if(L>m-tree[2*k].Rlcis+1&&m+tree[k*2+1].Llcis<=R)//注意这个条件

                maxlen=max(m-L+1+tree[k*2+1].Llcis,maxlen);

            else maxlen=R-L+1;//最长的升序长度

    }

}

int main()

{

    int t,n,m,QL,QR;

    char c[2];

    scanf("%d",&t);

    while(t--)

    {

        scanf("%d%d",&n,&m);

        for(int i=1;i<=n;i++)

        scanf("%d",&num[i]);

        build(1,n,1);

        while(m--)

        {

            scanf("%s%d%d",c,&QL,&QR);

            if(c[0]=='U') updata(1,n,1,QL+1,QR);

            if(c[0]=='Q')

            {

                maxlen=0; find(1,n,1,QL+1,QR+1);

                printf("%d\n",maxlen);

            }

        }

    }

}



你可能感兴趣的:(HDU)