線段樹常規操作

關於線段樹的區間修改(加值), 區間查詢(求和), 利用延遲化的思想, 是比較容易的.

主要是想好標記下傳中lazy-tag的意義是什麼, 這個思路中的add標記的意義是: 當前節點沒有問題, 但是子節點要受到當前節點標記的影響.

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 600;
ll s[MAXN << 2 | 1], add_flag[MAXN << 2 | 1];
ll a[MAXN], v;
int ql, qh;
inline void down(int o, int lo, int hi){
    if(add_flag[o]){// 我們保證了訪問某個節點時, 他的祖先節點不存在任何標記
        add_flag[o << 1] += add_flag[o];
        add_flag[o << 1 | 1] += add_flag[o];

        int mi = lo + hi >> 1;
        s[o << 1] += (mi - lo) * add_flag[o];
        s[o << 1 | 1] += (hi - mi) * add_flag[o];

        add_flag[o] = 0;
    }
}
inline void up(int o){
    s[o] = s[o << 1] + s[o << 1 | 1];
}
void build(int o, int lo, int hi){
    if(hi - lo < 2){
        s[o] = a[lo];
        return;
    }
    int mi = lo + hi >> 1;
    build(o << 1, lo, mi);
    build(o << 1 | 1, mi, hi);
    up(o);
}
void modify(int o, int lo, int hi){
    if(ql <= lo && hi <= qh){// o節點的sum已經修改了, 記錄v值, 如果要訪問子區間, 需要傳遞v值
        s[o] += (hi - lo) * v;
        add_flag[o] += v;
        return;
    }
    if(qh <= lo || hi <= ql){
        return;
    }
    down(o, lo, hi);
    int mi = lo + hi >> 1;
    modify(o << 1, lo, mi);
    modify(o << 1 | 1, mi, hi);
    up(o);
}
ll query(int o, int lo, int hi){
    if(ql <= lo && hi <= qh){
        return s[o];
    }
    if(qh <= lo || hi <= ql){
        return 0;
    }
    down(o, lo, hi);
    int mi = lo + hi >> 1;
    ll rst = 0;
    rst += query(o << 1, lo ,mi);
    rst += query(o << 1 | 1, mi ,hi);
    return rst;
}
int main(){
    int N, Q;
    scanf("%d%d", &N, &Q);
    for(int i = 0; i < N;){
        scanf("%lld", a + ++i);
    }
    ++N;
    build(1, 1, N);
    char cmd;
    while(Q--){
        getchar();
        scanf("%c", &cmd);
        if(cmd == 'C'){
            scanf("%d%d%lld", &ql, &qh, &v);
            ++qh;
            modify(1, 1, N);
        }
        else{
            scanf("%d%d", &ql, &qh);
            ++qh;
            printf("%lld\n", query(1, 1, N));
        }
    }
    return 0;
}

計蒜客-黑白石頭


沙灘上有一些石頭,石頭的顏色是白色或者黑色。小羊會魔法,它能把連續一段的石頭,黑色變成白色,白色變成黑色。小羊喜歡黑色,她想知道某些區間中最長的連續黑石頭是多少個。

這個和算法競賽入門經典裏的動態區間最長連續子序列很像.

先用線段樹維護每段區間, 從區間左/右端的黑/白石子的長度和區間內最長連續黑/白石子的長度, 一共6個信息.

在線段樹中, 當前區間的最長連續黑色石頭的長度, 要麼完全在左孩子裏, 要麼完全在右孩子裏, 要麼就橫跨左孩子和右孩子.

如果橫跨左孩子和右孩子, 這時就需要知道左孩子的右端點的黑色石頭的長度, 同理右孩子.

白色石頭同理.

 

查詢的時候, 也是這麼組織的. 因爲查詢區間可以在線段樹上被分爲連續的log(n)個區間, 按照線段樹詢問的方式, 倆倆合併就知道真的查詢區間的最長的連續黑石頭的個數了.

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 1e5 + 600;
struct SGT_NODE{
    int rev;
    int max0, max1, left0, left1, right0, right1;
} s[MAXN << 2 | 1];
struct Q_NODE{
    int max, left, right;
    Q_NODE(int max, int left, int right): max(max), left(left), right(right){}
};
int ql, qh, v;
inline void change(int o){
    swap(s[o].max0, s[o].max1);
    swap(s[o].left0, s[o].left1);
    swap(s[o].right0, s[o].right1);
}
inline void down(int o, int lo, int hi){
    s[o].rev %= 2;
    if(s[o].rev){
        s[o << 1].rev += s[o].rev;
        s[o << 1 | 1].rev += s[o].rev;

        change(o << 1);// 要注意這裏, 這兩個子節點是必須要反轉的
        change(o << 1 | 1);

        s[o].rev = 0;
    }
}
inline void up(int o, int lo, int hi){
    int mi = (lo + hi) >> 1;
    int left = o << 1, right = o << 1 | 1;
    int leftSize = mi - lo, rightSize = hi - mi;

    s[o].max0 = max(s[left].right0 + s[right].left0, max(s[left].max0, s[right].max0));
    s[o].max1 = max(s[left].right1 + s[right].left1, max(s[left].max1, s[right].max1));

    s[o].left0 = s[left].left0 == leftSize? leftSize + s[right].left0: s[left].left0;
    s[o].left1 = s[left].left1 == leftSize? leftSize + s[right].left1: s[left].left1;

    s[o].right0 = s[right].right0 == rightSize? s[left].right0 + rightSize: s[right].right0;
    s[o].right1 = s[right].right1 == rightSize? s[left].right1 + rightSize: s[right].right1;
}
void build(int o, int lo, int hi){
    if(hi - lo < 2){
        scanf("%d", &v);
        if(v){
            s[o].max1 = s[o].left1 = s[o].right1 = 1;
            s[o].max0 = s[o].left0 = s[o].right0 = 0;
        }
        else{
            s[o].max0 = s[o].left0 = s[o].right0 = 1;
            s[o].max1 = s[o].left1 = s[o].right1 = 0;
        }
        return;
    }
    int mi = (lo + hi) >> 1;
    build(o << 1, lo, mi);
    build(o << 1 | 1, mi, hi);

    up(o, lo, hi);
}
void modify(int o, int lo, int hi){
    if(ql <= lo && hi <= qh){
        ++s[o].rev;
        swap(s[o].max0, s[o].max1);
        swap(s[o].left0, s[o].left1);
        swap(s[o].right0, s[o].right1);
        return;
    }
    if(qh <= lo || hi <= ql){
        return;
    }
    down(o, lo, hi);

    int mi = (lo + hi) >> 1;
    modify(o << 1, lo, mi);
    modify(o << 1 | 1, mi, hi);

    up(o, lo, hi);
}
Q_NODE query(int o, int lo, int hi){
    if(ql <= lo && hi <= qh){
        return Q_NODE(s[o].max1, s[o].left1, s[o].right1);
    }
    if(qh <= lo || hi <= ql){
        return Q_NODE(0, 0, 0);
    }
    down(o, lo, hi);

    int mi = (lo + hi) >> 1;
    Q_NODE left = query(o << 1, lo, mi), right =  query(o << 1 | 1, mi, hi);
    int leftSize = mi - lo, rightSize = hi - mi;
    Q_NODE rst(max(left.right + right.left, max(left.max, right.max)), 0, 0);
    rst.left = left.left == leftSize? leftSize + right.left: left.left;
    rst.right = right.right == rightSize? left.right + rightSize: right.right;

    return rst;
}
int main(){
    int N;
    scanf("%d", &N);
    ++N;
    build(1, 1, N);
    int q;
    scanf("%d", &q);
    while(q--){
        scanf("%d%d%d", &v, &ql, &qh);
        ++qh;
        if(v){
            modify(1, 1, N);
        }
        else{
            printf("%d\n", query(1, 1, N).max);
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章