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);
            }
        }
    }
}


posted @ 2013-10-08 21:14  pangbangb  阅读(213)  评论(0编辑  收藏  举报