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

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

时间:2019-10-16 21:45:23      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:线段树   pre   存储   cin   using   long   code   递增   初始   

题意:

给定n个整数。 你有两种操作: U A B:用B替换第A个数。(编号从0开始) Q A B:输出[a,b]中最长连续严格递增子序列(LCIS)的长度。

第一行有一个整数T,表示数据组数。 每组数据以两个整数n,m(0 <n,m <= 100000)开始。 下一行有n个整数(0 <= val <= 100000)。

接下来的m行每行表示一个操作: U A B(0 <= A,n,0 <= B = 100000) 或 Q A B(0 <= A <= B <n)。

对于每个询问,输出答案。

思路:

结点修改以及区间查询,我们很容易想到线段树去实现。同时要求 LCIS 我们会想到线段树存储区间最大值的操作。但是关键在于区间如何去更新。

当左右两个子树区间向上合并的时候,父亲区间的最大值应该怎么更新。

进行分类讨论:

(1)合并时中间部分不会连接:左右区间合并时由于 左子树的右端值大于等于右子树的左端值,所以中间部分不能连接 (这样该区间最大值就等于 左右子树区间的最大值)

(2)如果要进行连接:{我们就需要记录某个区间的左右端连续长度,以及左右端点的值}

 (i)判断是否更新 合并区间左右端连续长度,由于中间连接,所以如果左子树左端连续长度等于其区间长度,(即整个区间连续)则需要更新 合并区间左端长度 = 左子树左端连续长度+右子树左端连续长度

同理,合并区间右端长度 = 左子树右端连续长度+右子树右端连续长度

 (ii) 最后再次判断是否更新区间最长

 

code: 其他具题见代码注解

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double Pi = acos(-1.0);
const double esp = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 5e5+7;
const int maxm = 1e6+7;
const int mod = 1e9+7;
//区间合并问题
//要求得区间最长lcs,利用线段树及两两拆分
//合并后的最长长度为记录,左最长连续,右最长连续,和中间合并后的最长连续
int n,m;
struct segmentTree
{
    int arr[maxn];
    //区间最大lcis 
    int maxlen[maxn<<2];
       //左右边界值 
    int ls[maxn<<2];
    int rs[maxn<<2];
    //左右最大lcis 
    int lmax[maxn<<2];
    int rmax[maxn<<2];
    //结点从1开始到n
    int L[maxn<<2],R[maxn<<2];
    void init(int n){
        for(int i=1;i<=n;i++){
            scanf("%d",&arr[i]);
        }
    }
    void build(int l,int r,int p){
        L[p] = l;
        R[p] = r;
        if(l==r) { 
            lmax[p] = rmax[p] = maxlen[p] = 1;
            ls[p] = rs[p] = arr[l];    
            return ;
        }
        int mid = (l+r)>>1;
        build(l,mid,p<<1);
        build(mid+1,r,p<<1|1);
        push_up(p);
    }
    void push_up(int p){
        //对push_up()进行判断递推
        //(1)左右端点、左右端连续长度赋值,初始区间最大值 
        ls[p] = ls[p<<1];
        rs[p] = rs[p<<1|1];
        lmax[p] = lmax[p<<1];
        rmax[p] = rmax[p<<1|1];
        maxlen[p] = max(maxlen[p<<1],maxlen[p<<1|1]);
        //(2)中间部分长度是否合并 
        if(rs[p<<1]<ls[p<<1|1]){
            //(2*)判断是否更新该区间左右端连续最大长度 
            //(2**)如果左子树lcis等于其区间长度 
            if(lmax[p<<1] == (R[p<<1]-L[p<<1]+1)){
                lmax[p] += lmax[p<<1|1]; 
            }
            //(2**)如果右子树lcis等于其区间长度 
            if(rmax[p<<1|1] == (R[p<<1|1]-L[p<<1|1]+1)){
                rmax[p] += rmax[p<<1];
            }
            //(3)判断最大长度是否更新:左子树的右端与右子树的左端 
            maxlen[p] = max(maxlen[p],rmax[p<<1] + lmax[p<<1|1]);
        }
    } 
    void update(int p,int k,int q){
        if(L[p] == R[p]){
            //更新结点值 
            ls[p] = rs[p] = k;
            return ;
        }
        int mid = (L[p]+R[p])>>1;
        if(q<=mid) update(p<<1,k,q);
        else update(p<<1|1,k,q);
        push_up(p); 
    }
    int query(int ql,int qr,int p){
        if(L[p]>=ql && R[p]<=qr){
            return maxlen[p];
        }
        int mid = (L[p]+R[p])>>1;
        int res = 0;
        if(ql<=mid) res = max(res,query(ql,qr,p<<1));
        if(qr>mid) res = max(res,query(ql,qr,p<<1|1));
        //(中间区间长度)
        if(rs[p<<1]<ls[p<<1|1]){
            res = max(res,min(mid-ql+1,rmax[p<<1])+min(qr-mid,lmax[p<<1|1]));
        }
        return res;
    }
}seg;

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        seg.init(n);
        seg.build(1,n,1);
        string s;
        for(int i=0;i<m;i++){
            cin>>s;
            if(s[0]==Q){
                int L,R;
                scanf("%d %d",&L,&R);
                printf("%lld\n",seg.query(L+1,R+1,1));
            }else{
                int ith,k;
                scanf("%d %d",&ith,&k);
                seg.update(1,k,ith+1);
            }
        }
    }
    
    return 0;
}

 

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

标签:线段树   pre   存储   cin   using   long   code   递增   初始   

原文地址:https://www.cnblogs.com/Tianwell/p/11688519.html

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