码迷,mamicode.com
首页 > 其他好文 > 详细

HDU3308 线段树区间合并

时间:2015-04-04 18:06:38      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:

  题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 ,简单的线段树区间合并。

  线段树的区间合并:一般是要求求最长连续区间,在PushUp()函数中实现区间合并操作。

 


解法:

  由于对于一个区间的最长序列来说,最优解要么完全在左半序列,要么完全在右半序列,要么跨越中间点。所以可以构造线段树,维护结点区间的三个元素:最长上升前缀len[].l,最长上升后缀len[].r,最长上升序列len[].m。所以对于一个区间来说,有这样两种情况:

1. 左儿子最右边的值 >= 右儿子最左边的值(不能区间合并)

  前缀 = 左儿子的前缀    len[rt].l = len[rt << 1].l

  后缀 = 右儿子的后缀    len[rt].r = len[rt << 1 | 1].r

  最长序列 = 左右儿子的最长序列的最大值    len[rt].m = max(len[rt << 1].m , len[rt << 1 | 1].m)

2. 左儿子最右边的值 < 右儿子最左边的值(可以区间合并)

  前缀 = (左儿子的前缀 == 左儿子的长度) ? 左儿子的前缀 + 右儿子的前缀 : 左儿子的前缀

  后缀 = (右儿子的后缀 == 右儿子的长度) ? 右儿子的后缀 + 左儿子的后缀 : 右儿子的后缀

  最长序列 = max(左儿子的后缀 + 右儿子的前缀 , 左儿子的最长序列, 右儿子的最长序列)

  

  还有要注意的是query()函数,在查询的时候,完全在区间左半边或者完全在区间右半边的情况比较好办,如果是两边都有的话这样来考虑:

1.区间不能合并:这种情况可以直接返回左右儿子查询的最大值即可;

2.区间可以合并:左右儿子合并后的长度、左右儿子的查询,这三者取最大值;这里要注意合并左右儿子的时候不能超过查询区间的长度。

 

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
#define flag a[m + 1] > a[m]    //区间合并的标志
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 100000 + 5;
struct Max_len {    //三个元素
    int l , r , m;
} len[maxn << 2];
int a[maxn];

void PushUp(int l , int r , int rt)
{          //在PushUp中实现区间合并
    int m = (l + r) >> 1;
    len[rt].l = len[rt << 1].l;
    if(flag && len[rt].l == (m - l + 1))
        len[rt].l += len[rt << 1 | 1].l;
    
    len[rt].r = len[rt << 1 | 1].r;
    if(flag && len[rt].r == (r - m))
        len[rt].r += len[rt << 1].r;

    if(flag) {
        len[rt].m = max(len[rt << 1].r + len[rt << 1 | 1].l , 
            max(len[rt << 1].m , len[rt << 1 | 1].m));
    } else {
        len[rt].m = max(len[rt << 1].m , len[rt << 1 | 1].m);
    }
}
void build(int l , int r , int rt)
{
    if(l == r) {
        scanf("%d" , &a[l]);
        len[rt].l = len[rt].r = len[rt].m = 1;
        return;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUp(l , r , rt);
}
void update(int p , int x , int l , int r , int rt) 
{
    if(l == r) {
        a[p] = x;
        return;
    }
    int m = (l + r) >> 1;
    if(p <= m)
        update(p , x , lson);
    else
        update(p , x , rson);
    PushUp(l , r , rt);
}
int query(int L , int R , int l , int r , int rt)
{
    if(L <= l && R >= r) {
        return len[rt].m;
    }
    int m = (l + r) >> 1;
    if(R <= m)
        return query(L , R , lson);
    else if(L > m)
        return query(L , R , rson);
    else {
        int ll = query(L , R , lson);
        int rr = query(L , R , rson);
        int ret = max(ll , rr);
        if(flag) {
            ll = min(len[rt << 1].r , m - L + 1);    //不能超过查询区间的长度
            rr = min(len[rt << 1 | 1].l , R - m);
            ret = max(ret , ll + rr);
        }
        return ret;
    }
}
int main() 
{
    int n , m , T;
    int a , b;
    char ch[3];
    cin >> T;
    while(T--) {
        scanf("%d %d" , &n , &m);
        build(0 , n - 1 , 1);
        while(m--) {
            scanf("%s" , ch);
            if(ch[0] == U) {
                scanf("%d %d" , &a , &b);
                update(a , b , 0 , n - 1 , 1);
            } else {
                scanf("%d %d" , &a , &b);
                printf("%d\n" , query(a , b , 0 , n - 1 , 1));
            }
        }
    }
    return 0;
}

 

 

 

HDU3308 线段树区间合并

标签:

原文地址:http://www.cnblogs.com/H-Vking/p/4392533.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!