zoukankan      html  css  js  c++  java
  • HDU

    题意:

    给定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
    ",seg.query(L+1,R+1,1));
                }else{
                    int ith,k;
                    scanf("%d %d",&ith,&k);
                    seg.update(1,k,ith+1);
                }
            }
        }
        
        return 0;
    }
  • 相关阅读:
    Vjios P1736 铺地毯【暴力,思维】
    Vijos P1116 一元三次方程求解【多解,暴力,二分】
    Vijos P1131 最小公倍数和最大公约数问题【暴力】
    Vijos P1786 质因数分解【暴力】
    C++课程设计类作业4
    析构函数的用法【简单理论讲解】
    C++课程设计类作业3
    最短路径Floyd算法【图文详解】
    HDU 1874 畅通工程续【Floyd算法实现】
    C++课程设计类作业2
  • 原文地址:https://www.cnblogs.com/Tianwell/p/11688519.html
Copyright © 2011-2022 走看看