HDU - 3308 LCIS (線段樹區間合併)

題意:c++

給定n個整數。 你有兩種操做: U A B:用B替換第A個數。(編號從0開始) Q A B:輸出[a,b]中最長連續嚴格遞增子序列(LCIS)的長度。ui

第一行有一個整數T,表示數據組數。 每組數據以兩個整數n,m(0 <n,m <= 100000)開始。 下一行有n個整數(0 <= val <= 100000)。 spa

接下來的m行每行表示一個操做: U A B(0 <= A,n,0 <= B = 100000) 或 Q A B(0 <= A <= B <n)。code

對於每一個詢問,輸出答案。blog

思路:ci

結點修改以及區間查詢,咱們很容易想到線段樹去實現。同時要求 LCIS 咱們會想到線段樹存儲區間最大值的操做。可是關鍵在於區間如何去更新。string

當左右兩個子樹區間向上合併的時候,父親區間的最大值應該怎麼更新。it

進行分類討論:class

(1)合併時中間部分不會鏈接:左右區間合併時因爲 左子樹的右端值大於等於右子樹的左端值,因此中間部分不能鏈接 (這樣該區間最大值就等於 左右子樹區間的最大值)date

(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;
}
相關文章
相關標籤/搜索