1. 程式人生 > >淺談一類線段樹轉移的問題

淺談一類線段樹轉移的問題

維護 長度 思維 putchar 目的 以及 最大值 continue else if

最近大概是泛做了線段樹相關題目,但是這些線段樹大概都需要比較強的思維和比較長的代碼……\(2333\)

$\rm{Task1} $子段和

其實這個算是比較簡單的了,畢竟\(qyf\)曾經給我們講過,當時我就覺得十分的……麻煩233.

那麽例題其實就是\(\rm{SPOJ}\)\(GSS\)系列——的前三道題(後幾道題都不會做)

\(GSS1\)區間求最大子段和(不帶修)

\(Link\)

\(2333\)應該算是比較簡單的了,我們對於每個區間維護一個區間和,維護一個從最左端開始且必須包含最左端元素的最大子段和,再維護一個從最右端開始且必須包含最右端元素的最大子段和,最後維護一個區間最大子段和

那麽轉移(\(push\_up\))時就顯得十分方便。我們的父區間的\(Lmax\)只取決於左子區間的\(Lmax\)當左區間的\(Sum\)等於\(Max\)時(即左區間全部都要納入到其最大子段和中時),左區間的\(Sum\)與右區間的\(Lmax\)的和。那麽對於區間的\(Rsum\),也是一個道理。最終對於該區間的最大子段和,我們不考慮從已經轉移來的\(Lmax/Rmax\),而是考慮從左右區間的\(Max\)以及左右區間的和來轉移。大體代碼:

inline void P_u(int rt){
    S(rt) = S(ls(rt)) + S(rs(rt)) ;
    Lsum(rt) = max(Lsum(ls(rt)), S(ls(rt)) + Lsum(rs(rt))) ;
    Rsum(rt) = max(Rsum(rs(rt)), S(rs(rt)) + Rsum(ls(rt))) ;
    Sum(rt) = max(max(Sum(ls(rt)), Sum(rs(rt))), Lsum(rs(rt)) +            Rsum(ls(rt))) ;
}

還有值得註意的一點:在詢問的時候,它比較膈應……就是由於是連續的,所以你不能直接像普通的線段樹一樣詢問然後加起來……所以所就要類似於邊詢問,邊\(push\_up\)這種感覺。

Tree query(int rt, int l, int r){
    if (L <= l && r <= R) 
        return T[rt] ; 
    Tree res, A, B ;
    if (mid >= R) return query(ls(rt), l, mid) ;
    if (mid < L) return query(rs(rt), mid + 1, r) ; 
    A = query(ls(rt), l, mid), B = query(rs(rt), mid + 1, r) ;
    res.Lsum = max(A.Lsum, A.S + B.Lsum) ; res.Rsum = max(B.Rsum, B.S + A.Rsum) ;
    res.Sum = max(max(A.Sum, B.Sum), A.Rsum + B.Lsum) ; res.S = A.S + B.S ; return res ;
}

然後總代碼:

#include <cstdio>
#include <iostream>
#define MAXN 200010
#define LL long long
#define max my_Fuckmax
#define ls(rt) rt << 1
#define rs(rt) rt << 1 | 1
#define  mid ((l + r) >> 1)

using namespace std ;
struct Tree{
    LL S, Sum, Lsum, Rsum ;
}T[MAXN << 1] ; int N, M, L, R, base[MAXN] ; 

#define S(x) T[x].S
#define Sum(x) T[x].Sum
#define Lsum(x) T[x].Lsum
#define Rsum(x) T[x].Rsum


inline LL my_Fuckmax(LL A, LL B){
    return A & ((B - A) >> 63) | B & ((~(B - A)) >> 63) ;
}
inline void P_u(int rt){
    S(rt) = S(ls(rt)) + S(rs(rt)) ;
    Lsum(rt) = max(Lsum(ls(rt)), S(ls(rt)) + Lsum(rs(rt))) ;
    Rsum(rt) = max(Rsum(rs(rt)), S(rs(rt)) + Rsum(ls(rt))) ;
    Sum(rt) = max(max(Sum(ls(rt)), Sum(rs(rt))), Lsum(rs(rt)) + Rsum(ls(rt))) ;
}
inline void build(int rt, int l, int r){
    if (l == r){
        T[rt].Lsum = T[rt].Rsum = 
        T[rt].S = T[rt].Sum = base[l] ; return  ;
    } build(ls(rt), l, mid), build(rs(rt), mid + 1, r), P_u(rt) ;
}
Tree query(int rt, int l, int r){
    if (L <= l && r <= R) 
        return T[rt] ; 
    Tree res, A, B ;
    if (mid >= R) return query(ls(rt), l, mid) ;
    if (mid < L) return query(rs(rt), mid + 1, r) ; 
    A = query(ls(rt), l, mid), B = query(rs(rt), mid + 1, r) ;
    res.Lsum = max(A.Lsum, A.S + B.Lsum) ; res.Rsum = max(B.Rsum, B.S + A.Rsum) ;
    res.Sum = max(max(A.Sum, B.Sum), A.Rsum + B.Lsum) ; res.S = A.S + B.S ; return res ;
}
int main(){
    cin >> N ; register int i ;
    for (i = 1 ; i <= N ; ++ i) scanf("%d", &base[i]) ; build(1, 1, N) ; cin >> M ;
    while (M --) scanf("%d%d", &L, &R), printf("%lld\n", query(1, 1, N).Sum) ; return 0 ;
}

\(GSS3\)區間求最大子段和(帶修)

\(Link\)

……其實吧,這個帶修不帶修……好像影響並不大?

#include <cstdio>
#include <iostream>
#define MAXN 200010
#define LL long long
#define max my_Fuckmax
#define ls(rt) rt << 1
#define rs(rt) rt << 1 | 1
#define  mid ((l + r) >> 1)

using namespace std ;
bool MArk ;
struct Tree{
    LL S, Sum, Lsum, Rsum ;
}T[MAXN << 1] ; int N, M, L, R, base[MAXN] ; 

#define S(x) T[x].S
#define Sum(x) T[x].Sum
#define Lsum(x) T[x].Lsum
#define Rsum(x) T[x].Rsum


inline LL my_Fuckmax(LL A, LL B){
    return A & ((B - A) >> 63) | B & ((~(B - A)) >> 63) ;
}
inline void P_u(int rt){
    S(rt) = S(ls(rt)) + S(rs(rt)) ;
    Lsum(rt) = max(Lsum(ls(rt)), S(ls(rt)) + Lsum(rs(rt))) ;
    Rsum(rt) = max(Rsum(rs(rt)), S(rs(rt)) + Rsum(ls(rt))) ;
    Sum(rt) = max(max(Sum(ls(rt)), Sum(rs(rt))), Lsum(rs(rt)) + Rsum(ls(rt))) ;
}
inline void build(int rt, int l, int r){
    if (l == r){
        T[rt].Lsum = T[rt].Rsum = 
        T[rt].S = T[rt].Sum = base[l] ; return  ;
    } build(ls(rt), l, mid), build(rs(rt), mid + 1, r), P_u(rt) ;
}
Tree query(int rt, int l, int r){
    if (L <= l && r <= R) 
        return T[rt] ; 
    Tree res, A, B ;
    if (mid >= R) return query(ls(rt), l, mid) ;
    if (mid < L) return query(rs(rt), mid + 1, r) ; 
    A = query(ls(rt), l, mid), B = query(rs(rt), mid + 1, r) ;
    res.Lsum = max(A.Lsum, A.S + B.Lsum) ; res.Rsum = max(B.Rsum, B.S + A.Rsum) ;
    res.Sum = max(max(A.Sum, B.Sum), A.Rsum + B.Lsum) ; res.S = A.S + B.S ; return res ;
}
inline void change(int rt, int l, int r, int k){
    if (L == l && L == r){
        T[rt].Sum = T[rt].Lsum = T[rt].Rsum = T[rt].S = k ; return ;
    }
    if (L <= mid) change(ls(rt), l, mid, k) ;
    else change(rs(rt), mid + 1, r, k ) ; P_u(rt) ;
}
int main(){
    cin >> N ; register int i ;
    for (i = 1 ; i <= N ; ++ i) scanf("%d", &base[i]) ; build(1, 1, N) ; cin >> M ;
    while (M --) {
        scanf("%d%d%d", &MArk, &L, &R) ;
        if (!MArk) change(1, 1, N, R) ;
        else printf("%lld\n", query(1, 1, N).Sum) ;
    }
}

\(GSS4\)區間開根問題

\(Link\)

這個和子段和一點兒關系都沒有,順便整一下233.

就是讓你區間開根(向下取整)+查詢……這個東西大概就是對於一個\(2^{63}\)內的數值\(N\),我們假設其開根\(k\)次可以得到\(N < 2\)——只要\(N < 2\)之後的計算就會集中在一個緊確的範圍\((1,2)\)內,向下取整之後永遠都會是\(1\),所以我們不需要再去考慮。

那麽現在,我們致力於去確定\(k\)的值域。我們不得不承認,\(\sqrt n\)\([0,+\infty]\)是單調遞增的,同理三次方根也是,四次方根也是……所以我們不妨取最大值,考慮\(N=2^{63}\)時,\(k\)值的大小。而很顯然,此時的\(k\)應該為\(\log _263+1 ≈ 6.978\)——這似乎是十分平凡的結論。

總之,我們得出,似乎運算次數的上界就是\(k≈7\),所以說我們直接暴力除就好了,聚合分析一下,復雜度的上界似乎是\(\Omega(7n)\)的樣子,無非就是多幾個常數。

#include <cmath>
#include <cstdio>
#include <iostream>
#define MAXN 500010
#define ll long long
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
#define mid ((l + r) >> 1)

using namespace std ;
ll N, M, K, i, L, R ; 
ll base[MAXN], T[MAXN], tot ;

inline ll qr(){
    ll k = 0 ; char c = getchar() ;
    while (!isdigit(c)) c = getchar() ;
    while (isdigit(c)) k = k * 10 + c - 48, c = getchar() ;
    return k  ; 
}
inline void p_u(ll rt){ ;}
void _Build(ll rt, ll l, ll r){
    if (l == r){T[rt] = base[l] ; return ;}
    _Build(ls(rt), l, mid) ;
    _Build(rs(rt), mid + 1, r) ;
    T[rt] = T[ls(rt)] + T[rs(rt)] ;
}
inline ll _query(ll rt, ll l, ll r, ll sl, ll sr){
    if (l >= sl && r <= sr) return T[rt] ;
    ll ret = 0 ;
    if (mid >= sl) ret += _query(ls(rt), l, mid, sl, sr) ;
    if (mid < sr) ret += _query(rs(rt), mid + 1, r, sl, sr) ;
    return ret ;
}
inline void _Sqrt(ll rt, ll l, ll r, ll sl, ll sr){
    if (l >= sl && r <= sr){
        if (T[rt] <= (r - l + 1)) return ; 
        else {
            if (l == r){
                T[rt] = (int)(pow((double)T[rt], 0.5)) ;
                return ;
            }
        }
    }
    if (mid >= sl) _Sqrt(ls(rt), l, mid, sl, sr), 
                   T[rt] = T[ls(rt)] + T[rs(rt)] ;
    if (mid < sr) _Sqrt(rs(rt), mid + 1, r, sl, sr), 
                   T[rt] = T[ls(rt)] + T[rs(rt)] ;
}
int main(){
    while(cin >> N){
        ++ tot, printf("Case #%d:\n", tot) ;
        for (i = 1; i <= N ; ++ i) base[i] = qr() ;
        _Build(1, 1, N) ; cin >> M ;
        for (i = 1; i <= M ; ++ i){
            K = qr(), L = qr(), R = qr() ;
            if (L > R) swap(L, R) ;
            if (K){
                printf("%lld\n", _query(1, 1, N, L, R)) ;
                continue ;
            }
            _Sqrt(1, 1, N, L, R) ;
        }
        printf("\n") ;
    }
    return 0 ;
}

\(\rm{Task2}\) 最長連續問題

這個東西其實應該跟最大子段和差不多——要求的都是連續的東西。對於所有包括連續字樣的東西,基本的思路大概都是維護一個從左端開始的,維護一個從右端開始的,然後從下向上不斷\(push_up\)即可。

\(emmm\)在這邊整理幾道思路不錯的題吧:

\(\rm{USACO}\) 酒店(\(hotel\))

\(Link\)

初始的一個全零的序列,我們對它準確來說有以下三個操作:

  • 區間置\(0\)
  • 區間置\(1\)
  • 詢問是否有一段長度為\(k\)的連續的零區間,如果有的話,選取最靠左的,輸出其左端點並執行操作②

這個題在我看來,應該算是一個思維題。對於最後一個操作,我十分地懵逼——因為我壓根不知道該怎麽維護。

但事實上……這就是學數據結構學傻了的後果……畢竟數據結構只會是一個輔助而已。仔細想來,好像除了權值線段樹能夠維護\(\rm{DP}\)之外,沒做過什麽數據結構的好題,都是一些數據結構的裸題……大概這就是學傻了吧,只會專一的一門學科,或者說只會專精一種東西——還是十分蠢笨遲鈍地“專精”。

唉,大概檢驗一個人學沒學過數據結構,不是通過他會不會做類似於\(NOI2005\)維護數列那樣的毒瘤裸題,而是看他到底可不可以和其他的東西結合在一起。學習大抵也是同樣的道理,不可以把學的東西遷移到其他地方,照樣是白學吧。

誒,好像扯了什麽奇怪的東西……

回到正題,我們不考慮直接維護這個東西,而是通過維護區間內的最長連續\(0\)的個數,達到輔助查找區間的目的。那麽我們查找區間的時候,就直接**擇最左邊的區間優先,並\(check\)其是否有足夠的\(0\)

對於查詢,我們先查詢左區間,再查詢中間(左區間的右邊與右區間的並集),最後查詢右區間。

#include <cstdio>
#include <iostream>
#define MAXN 200100
#define ls(x) x << 1
#define rs(x) x << 1 | 1
#define  mid  ((l + r) >> 1)

using namespace std ;
struct Tree{
    int Sum, Len, Lsum, Rsum, tag ; 
}T[MAXN << 1] ; int A, B, N, M, MArk, i, t ;

inline void push_down(int rt){
    if (T[rt].tag == -1) return ;
    else if (T[rt].tag == 0){
        T[ls(rt)].tag = T[rs(rt)].tag = 0 ;
        T[ls(rt)].Lsum = T[ls(rt)].Sum = T[ls(rt)].Rsum = 0 ;
        T[rs(rt)].Lsum = T[rs(rt)].Sum = T[rs(rt)].Rsum = 0 ;
    }
    else{
        T[ls(rt)].tag = T[rs(rt)].tag = 1 ;
        T[ls(rt)].Lsum = T[ls(rt)].Sum = T[ls(rt)].Rsum = T[ls(rt)].Len ;
        T[rs(rt)].Lsum = T[rs(rt)].Sum = T[rs(rt)].Rsum = T[rs(rt)].Len ;   
    }
    T[rt].tag = -1 ;
}
inline void push_up(int rt){
    if (T[ls(rt)].Sum == T[ls(rt)].Len) 
        T[rt].Lsum = T[ls(rt)].Len + T[rs(rt)].Lsum ;
    else T[rt].Lsum = T[ls(rt)].Lsum ;
    if (T[rs(rt)].Sum == T[rs(rt)].Len) 
        T[rt].Rsum = T[rs(rt)].Len + T[ls(rt)].Rsum ;
    else T[rt].Rsum = T[rs(rt)].Rsum ;
    T[rt].Sum = max(T[ls(rt)].Sum, T[rs(rt)].Sum), 
    T[rt].Sum = max(T[rt].Sum, T[ls(rt)].Rsum + T[rs(rt)].Lsum) ;
} 
void build(int rt, int l, int r){
    T[rt].tag = -1,
    T[rt].Len = T[rt].Lsum = 
    T[rt].Rsum = T[rt].Sum = r - l + 1 ;
    if (l == r){ return ; }
    build(ls(rt), l, mid), build(rs(rt), mid + 1, r) ;
}
void update(int rt, int l, int r, int ul, int ur, int k){
    if (ul <= l && ur >= r){
        T[rt].tag = k ;
        if (k == 0) T[rt].Lsum = T[rt].Rsum = T[rt].Sum = 0 ;
        else T[rt].Lsum = T[rt].Rsum = T[rt].Sum = T[rt].Len ;
        return ;
    }
    push_down(rt) ;
    if (ul <= mid) update(ls(rt), l, mid, ul, ur, k) ;
    if (ur > mid) update(rs(rt), mid + 1, r, ul, ur, k) ;
    push_up(rt) ;
}
int query(int rt, int l, int r){
    push_down(rt) ;
    if (l == r) return l ;
    if (T[ls(rt)].Sum >= A) return query(ls(rt), l, mid) ;
    else if (T[ls(rt)].Rsum + T[rs(rt)].Lsum >= A) return mid - T[ls(rt)].Rsum + 1 ;
    return query(rs(rt), mid + 1, r) ;
}
int main(){
    cin >> N >> M ;
    build(1, 1, N) ;
    while(M --){
        scanf("%d", &MArk) ;
        if (MArk == 2)  scanf("%d%d", &A, &B), update(1, 1, N, A, A + B - 1, 1) ;   
        else {
            scanf("%d", &A) ;
            if (T[1].Sum >= A)
                printf("%d\n", t = query(1, 1, N)), update(1, 1, N, t, t + A - 1, 0)  ;
            else 
                putchar('0'), putchar('\n') ;
        }
    }  return 0 ;
} 

\(emmm\)這個題碼量其實不大,思維含量也不高,但是成功地把做數據結構題做傻了的我拉回了正途。

\(\rm{SCOI}\) 序列操作

\(Link\)

對於一個\(01\)序列,大體是這幾種操作:

  • 區間清零
  • 區間置為\(1\)
  • 區間全部取非
  • 區間查詢\(1\)的個數
  • 區間查詢最長連續的\(1\)的長度

好的,這道題被我秒了,爽啊……不過秒是秒了,對拍調試法調了好久\(233\)

其實對於\(1,2,4,5\)都好說,只是第\(3\)個操作,需要再另維護區間最長連續的\(0\)的長度,如果存在取非標記生效,就交換一下就行。

#include <cstdio>
#include <iostream>
#define MAX 200010
#define ls(x) x << 1 
#define rs(x) x << 1 | 1
#define mid ((l + r) >> 1)

using namespace std ;
struct Tree{
    int OS, OL, OR ;
    int Sum, Lsum, Len, Rsum, S, tag, t ;
    //tag = 1 -> 1,tag = 0 -> 0, tag = 2 -> xor
}T[MAX << 1] ; int N, M, MArk, L, R, base[MAX], i ;

inline void up(int rt){//
    T[rt].S = T[ls(rt)].S + T[rs(rt)].S ;
    //1
    if (T[ls(rt)].S == T[ls(rt)].Len) 
          T[rt].Lsum = max(T[ls(rt)].Lsum, T[ls(rt)].Len + T[rs(rt)].Lsum) ;
    else  T[rt].Lsum = T[ls(rt)].Lsum ;
    if (T[rs(rt)].S == T[rs(rt)].Len) 
          T[rt].Rsum = max(T[rs(rt)].Rsum, T[rs(rt)].Len + T[ls(rt)].Rsum) ;
    else  T[rt].Rsum = T[rs(rt)].Rsum ;
    T[rt].Sum = max(T[ls(rt)].Rsum + T[rs(rt)].Lsum, max(T[ls(rt)].Sum, T[rs(rt)].Sum)) ;
    //0
    if (!T[ls(rt)].S) 
          T[rt].OL = max(T[ls(rt)].OL, T[ls(rt)].Len + T[rs(rt)].OL) ;
    else  T[rt].OL = T[ls(rt)].OL ;
    if (!T[rs(rt)].S) 
          T[rt].OR = max(T[rs(rt)].OR, T[rs(rt)].Len + T[ls(rt)].OR) ;
    else  T[rt].OR = T[rs(rt)].OR ;
    T[rt].OS = max(T[ls(rt)].OR + T[rs(rt)].OL, max(T[ls(rt)].OS, T[rs(rt)].OS)) ;
}
inline void down(int rt){//
    if (T[rt].tag == -1) return ;
    if (T[rt].t == 1){
        T[ls(rt)].tag ^= 1, T[rs(rt)].tag ^= 1 ;
        T[ls(rt)].S = T[ls(rt)].Len - T[ls(rt)].S ;
        T[rs(rt)].S = T[rs(rt)].Len - T[rs(rt)].S ;
        //l
        T[ls(rt)].Sum ^= T[ls(rt)].OS ^= T[ls(rt)].Sum ^= T[ls(rt)].OS ;
        T[ls(rt)].Lsum ^= T[ls(rt)].OL ^= T[ls(rt)].Lsum ^= T[ls(rt)].OL ;
        T[ls(rt)].Rsum ^= T[ls(rt)].OR ^= T[ls(rt)].Rsum ^= T[ls(rt)].OR ;
        //r
        T[rs(rt)].Sum ^= T[rs(rt)].OS ^= T[rs(rt)].Sum ^= T[rs(rt)].OS ;
        T[rs(rt)].Lsum ^= T[rs(rt)].OL ^= T[rs(rt)].Lsum ^= T[rs(rt)].OL ;
        T[rs(rt)].Rsum ^= T[rs(rt)].OR ^= T[rs(rt)].Rsum ^= T[rs(rt)].OR ;  
    }
    if (T[rt].tag == 0){
        T[ls(rt)].tag = T[rs(rt)].tag = 0 ;
        T[ls(rt)].OL = T[ls(rt)].OR = T[ls(rt)].OS = T[ls(rt)].Len ;
        T[rs(rt)].OL = T[rs(rt)].OR = T[rs(rt)].OS = T[rs(rt)].Len ;
        T[ls(rt)].Lsum = T[ls(rt)].Rsum = T[ls(rt)].Sum = T[ls(rt)].S = 0 ;
        T[rs(rt)].Lsum = T[rs(rt)].Rsum = T[rs(rt)].Sum = T[rs(rt)].S = 0 ;
    }
    if (T[rt].tag == 1){
        T[ls(rt)].tag = T[rs(rt)].tag = 1 ;
        T[ls(rt)].OL = T[ls(rt)].OR = T[ls(rt)].OS = 0 ;
        T[rs(rt)].OL = T[rs(rt)].OR = T[rs(rt)].OS = 0 ;
        T[ls(rt)].Lsum = T[ls(rt)].Rsum = T[ls(rt)].Sum = T[ls(rt)].S = T[ls(rt)].Len ;
        T[rs(rt)].Lsum = T[rs(rt)].Rsum = T[rs(rt)].Sum = T[rs(rt)].S = T[rs(rt)].Len ;
    }
    T[rt].tag = -1, T[rt].t = 0 ; 
}
void _change(int rt, int l, int r, int k){//
    if (L <= l && r <= R){
        T[rt].tag = k ;
        if (!k) 
            T[rt].OL = T[rt].OR = T[rt].OS = T[rt].Len, 
            T[rt].Lsum = T[rt].Rsum = T[rt].Sum = T[rt].S = 0 ;
        else  T[rt].OL = T[rt].OR = T[rt].OS = 0, 
              T[rt].Lsum = T[rt].Rsum = T[rt].Sum = T[rt].S = T[rt].Len ;
        return ;
    }
    down(rt) ;
    if (L <= mid) _change(ls(rt), l, mid, k) ;
    if (R > mid) _change(rs(rt), mid + 1, r, k) ;
    up(rt) ; 
}
void _reverse(int rt, int l, int r){//
    if (L <= l && r <= R){
        T[rt].t = 1 ;
        T[rt].Sum ^= T[rt].OS ^= T[rt].Sum ^= T[rt].OS ;
        T[rt].Lsum ^= T[rt].OL ^= T[rt].Lsum ^= T[rt].OL ;
        T[rt].Rsum ^= T[rt].OR ^= T[rt].Rsum ^= T[rt].OR ;  
        return ;
    }
    down(rt) ;
    if (L <= mid) _reverse(ls(rt), l, mid) ;
    if (R > mid) _reverse(rs(rt), mid + 1, r) ;
    up(rt) ; 
}
inline int Sum(int rt, int l, int r){//
    if (L <= l && R >= r) return T[rt].S ;
    down(rt) ; int res = 0 ;
    if (L <= mid) res += Sum(ls(rt), l, mid) ;
    if (R > mid) res += Sum(rs(rt), mid + 1, r) ;
    return res ;
}
inline void build(int rt, int l, int r){//
    T[rt].tag = -1 ;
    T[rt].Len = r - l + 1 ;
    if (l == r){
        if (!base[l]) T[rt].OL = T[rt].OR = T[rt].OS = 1 ;
        else  T[rt].Sum = T[rt].Lsum = T[rt].Rsum = T[rt].S = 1 ;
        return ;
    }
    build(ls(rt), l, mid), build(rs(rt), mid + 1, r), up(rt) ;
}
inline Tree query(int rt, int l, int r){
    if (L <= l && R >= r) return T[rt] ;
    Tree res, A, B ;    
    if (mid >= R) return query(ls(rt), l, mid) ;
    if (mid < L) return query(rs(rt), mid + 1, r) ;
    A = query(ls(rt), l, mid), B = query(rs(rt), mid + 1, r) ;
    res.Lsum = max(A.Lsum, A.S + B.Lsum) ; res.Rsum = max(B.Rsum, B.S + A.Rsum) ;
    res.Sum = max(max(A.Sum, B.Sum), A.Rsum + B.Lsum) ; res.S = A.S + B.S ; 
    return res ;
}
int main(){
    cin >> N >> M ;
    for (i = 1 ; i <= N ; ++ i) scanf("%d", &base[i]) ;
    build(1, 1, N) ;
    while (M --){
//      cout << M << " " << "qwerweafasdfsdf" << endl ;
        scanf("%d%d%d", &MArk, &L, &R),
        ++ L, ++ R ;
        if (MArk == 0) _change(1, 1, N, 0) ;
        else if (MArk == 1) _change(1, 1, N, 1) ;
        else if (MArk == 2) _reverse(1, 1, N) ;
        else if (MArk == 3) printf("%d\n", Sum(1, 1, N)) ;
        else printf("%d", query(1, 1, N).Sum), putchar('\n') ;
//      cout << " qwerweafasdfsdf " << endl ;
    }
}

\(push\_up\)真長啊\(233\)

艹完這個題是真的爽啊~

\(Task3~\) 總結一下

其實這東西和\(DP\)是一樣的吧?你只需要確定你想要維護什麽(等價於確定狀態),然後明確父子區間如何向上維護(等價於狀態之間如何轉移)。

嗯,萬物相同。

野馬也,塵埃也,生物之以息向吹也。天之蒼蒼,其正色耶?其遠而無所至極耶?

不知為什麽,突然想到了這句話。

\(\mathscr{The~End}\)

淺談一類線段樹轉移的問題