hdu 3308(线段树区间合并)

LCIS

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

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<=105).
The next line has n integers(0<=val<=105).
The next m lines each has an operation:
U A B(0<=A,n , 0<=B=105)
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

题意:求解区间 a - b之内的最大连续上升子序列

题解:线段树区间合并 ,主要是PushUp操作 和 查询操作 ,在线段树中最长子序列可能在左子树也可能在右子树 ,也可能从左子树到右子树

,所以我们在在树的结构体中增加三个变量(左边的最长区间,右边的最长区间,最长合区间).然后其余的详细解释都在代码中了,有兴趣的朋友可以

看看.主要是理解左子树的右子树 和 右子树的左子树是如何影响区间的合区间,然后查询操作一定要记得取最小值。

#include<iostream>
#include<cstdio>
#define N 100005
using namespace std;

int MAX(int i,int j){
    if(i>j) return i;
    return j;
}
int MIN(int i,int j){
    if(i<j) return i;
    return j;
}
struct Tree{
    int l,r;
    int lv,rv,mv;  ///lv(rv)表示从最左(右)边起的最长连续递增子序列长度,mv表示该区间最大频率
}tree[4*N];
int a[N];
void PushUp(int l,int r,int idx){
    tree[idx].lv = tree[idx<<1].lv;  ///默认为左儿子的lv
    tree[idx].rv = tree[idx<<1|1].rv; ///默认为右儿子的rv
    tree[idx].mv = MAX(tree[idx<<1].mv,tree[idx<<1|1].mv);///对于父区间的mv值,默认是先在左右子区间的mx值中取一个较大值
    int mid=(l+r)>>1;
    int len = r-l+1;  ///区间长度
    if(a[mid]<a[mid+1]){ ///两边可以进行合并
        ///如果左子区间的lv等于左子区间的长度且可以延伸到右子区间,那么父区间的lv值等于左子区间
        ///的lv加上右子区间的lv,否则,父区间的lv值就是左子区间的lv值,对于右子区间,同理
        if(tree[idx].lv==len-(len>>1)) tree[idx].lv +=tree[idx<<1|1].lv;
        if(tree[idx].rv==(len>>1)) tree[idx].rv +=tree[idx<<1].rv;
        ///果左右区间能够延续,则在当前值和左子区间的rv+右子区间的lv之间取较大值
        tree[idx].mv = MAX(tree[idx].mv,tree[idx<<1].rv+tree[idx<<1|1].lv);
    }
}
void build(int l,int r,int idx){
    tree[idx].l = l;
    tree[idx].r = r;
    if(l==r){
        tree[idx].lv = tree[idx].rv = tree[idx].mv = 1;
        return;
    }
    int mid = (l+r)>>1;
    build(l,mid,idx<<1);
    build(mid+1,r,idx<<1|1);
    PushUp(l,r,idx);
}

void update(int l,int r,int pos,int idx){ ///单点更新
    if(l==r) {
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) update(l,mid,pos,idx<<1);
    else update(mid+1,r,pos,idx<<1|1);
    PushUp(l,r,idx);
}
int query(int l,int r,int idx){
    if(tree[idx].l>=l&&tree[idx].r<=r){
        return tree[idx].mv;
    }
    int mid=(tree[idx].l+tree[idx].r)>>1;
    int ans = 0;
    ///分别在左子区间和右子区间查找,结果分别为x,y,如果左右两个区间不能延续,
    ///那肯定是在x,y中找一个最大值
    if(l<=mid) ans = MAX(ans,query(l,r,idx<<1)); ///x
    if(r>mid) ans = MAX(ans,query(l,r,idx<<1|1)); ///y
    ///如果能够延续,设两端延续区间长度为z,那么肯定是在 x,y,z找最大值,另外,对于左区间,我们要保证
    ///向左延续的区间(rv)肯定是在[l,mid]中的(不然超过我们要查询的区间了),所以长度必须要在 mid - l+1之内
    ///向右亦如此.
    if(a[mid]<a[mid+1]){
        ans = MAX(ans,MIN(mid-l+1,tree[idx<<1].rv)+MIN(r-mid,tree[idx<<1|1].lv)); ///z
    }
    return ans;
}
int main()
{
    int n,m;
    int tcase;
    scanf("%d",&tcase);
    while(tcase--){
        scanf("%d%d",&n,&m);
       for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
       }
       build(1,n,1);
       //for(int i=1;i<=n;i++) printf("%d ",tree[i].mv);
       //printf("\n");
       while(m--){
            char s[5];
            scanf("%s",s);
            if(s[0]==‘U‘){
                int b,c;
                scanf("%d%d",&b,&c);
                a[++b] = c;  ///由于题目下标从0开始,所以++
                update(1,n,b,1);
            }else {
                int b,c;
                scanf("%d%d",&b,&c);
                b++,c++;
                printf("%d\n",query(b,c,1));
            }
       }
    }
    return 0;
}
时间: 2024-10-20 13:03:00

hdu 3308(线段树区间合并)的相关文章

hdu 3308 线段树 区间合并+单点更新+区间查询

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6592    Accepted Submission(s): 2866 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

HDU 4339 线段树区间合并

Query Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2573    Accepted Submission(s): 851 Problem Description You are given two strings s1[0..l1], s2[0..l2] and Q - number of queries.Your task

HDU 3911 线段树区间合并

北京赛区快了,准备袭击数据结构和图论.倒计时 18天,线段树区间合并.维护一个最长连续.. 题意:给一个01串,以下有一些操作,问区间最长的连续的1的个数 思路:非常裸的线段树区间合并 #include<iostream> #include<cstdio> #include<map> #include<set> #include<cmath> #define lson id << 1 #define rson id <<

hdu 1806(线段树区间合并)

Frequent values Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1476    Accepted Submission(s): 541 Problem Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreasin

HDU 3308 LCIS (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1

hdu 3308 线段树单点更新 区间合并

http://acm.hdu.edu.cn/showproblem.php?pid=3308 学到两点: 1.以区间端点为开始/结束的最长......似乎在Dp也常用这种思想 2.分类的时候,明确标准逐层分类,思维格式: 条件一成立: { 条件二成立: { } else { } } else { 条件二成立: { } else { } } 上面的这种方式很清晰,如果直接想到那种情况iif(条件一 &条件二)就写,很容易出错而且把自己搞乱,或者情况不全,,,我就因为这WA了几次 3.WA了之后 ,

HDU 3308 线段树(区间合并)

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3896    Accepted Submission(s): 1766 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

HDU 3308 LCIS(最长连续上升子序列)(线段树区间合并)

题意:给你n个整数,有两种操作,U A B把第A个数变成B,Q A B查询区间[A,B]的最长连续上升序列. 思路:还是查询和更新操作,而且也是询问区间中满足条件的连续最长区间 ,所以是线段树区间合并类型的题,通法是开三棵线段树,一个记录此区间内的LCIS的最长长度,一个记录从左边第一个数开始的LCIS长度,另一个记录从右边最后一个数结尾的LCIS长度.然后试图找到父亲与儿子关系维护的递推关系式就好 本题中的递推关系是: 1. 左儿子最右边的值 < 右儿子最左边的值 lmx = (左儿子的lmx

HDU 3308 LCIS (经典区间合并)【线段树】

<题目链接> 题目大意: 给你一段序列,对其进行两种操作,一是修改某个序号的点的值:二是查询某个区间的LCIS(最长上升子序列). 解题分析: 线段树区间合并的典型例题,用求某个区间的LCIS时,需要比较三个值,一是左区间的LCIS,二是右区间的LCIS,三是左右子区间合并的LCIS.最重要的是第三点如何实现,实现第三点需要维护一个最长后缀上升子序列和最长前缀上升子序列. #include <cstdio> #include <cstring> #include <