Data Structure[1]

algorithm[15] 22527 Brine

我是誰

22527 鄭竣陽
Brine
BrineTW#7355
  • 你是不是忘記我是誰了ㄏㄏ
Index

小約定(1)

  • 今天的課程將以「左閉右開」來表示區間
  • 程式碼中的 \(l, r\) 代表的是 \([l, r)\) 這段區間
    • 也就是說,對於 \(a_l, a_r\) 間的最小值 \(a_i\),\(l \le i < r\)
  • 為什麼這樣寫?
  • 看了就知道了,比較舒服

小約定(2)

  • 上課請多提問,提問有助於學習
    • 而且我很久沒講課可能講錯
    • 我上課大家都不回我我很寂寞
Sparse Table

稀疏表

暖身

  • 區間極值詢問
    • 給定一個長度為 \(n\) 的數列 \(\langle a_k \rangle_{k = 1}^n = \langle a_1, a_2, a_3,..., a_n \rangle\)
    • 有 \(q\) 次詢問,每次有一個數對 \((l, r)\)
    • 對每次詢問回答 \(a_l \sim a_r\) 的最大值
    • 資料範圍
      • \(n \in [1, 10^5]\)
      • \(q \in [1, 10^5]\)
      • \(l, r \in [1, n]\)
      • \(l \le r\)
    • 問了就是砸線段樹
      • 那如果 \(q\) 再大一點呢?

真的要線段樹嗎

  • 區間極值詢問
    • 給定一個長度為 \(n\) 的數列 \(\langle a_k \rangle_{k = 1}^n = \langle a_1, a_2, a_3,..., a_n \rangle\)
    • 有 \(q\) 次詢問,每次有一個數對 \((l, r)\)
    • 對每次詢問回答 \(a_l \sim a_r\) 的最大值
    • 資料範圍
      • \(n \in [1, 10^5]\)
      • \(q \in [1, 2 \times 10^7]\)
      • \(l, r \in [1, n]\)
      • \(l \le r\)
  • 真的要線段樹嗎
    • 不一定不會過,但有必要嗎?

新的資料結構

  • 我們希望有一個函數 \(f(i, p) = \displaystyle \max_{k \in [i, i + 2^p)} a_k \)
    • \([i, i+ 2^p)\) 共有 \(2^p\) 項
    • 白話一點,我們希望能快速求得 \(a_i \sim a_{i + 2^p - 1}\) 的最大值
    • 有了這個可以做什麼?

用兩段得出任意長度

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[0,3)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[0,3)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[3, 6)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[3, 6)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[2, 8)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[2, 8)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[2, 3)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[2, 3)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[0, 9)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
[0, 9)

用兩段得出任意長度

0
1
2
3
4
5
6
7
8
9
  • 任意一段區間中的最大值都可以由兩條紅色線內的最大值求出
  • 只要能把詢問紅色線的速度提升就可以有好的複雜度!
    • 速度要多快?
    • \(\mathcal O(1)\) 好了

如何建表

  • 我們想要能夠 \(\mathcal O(1)\) 查詢,但建表可以慢慢來
  • 聰明如你會發現:
    • 長度為 \(2^{p + 1}\) 的區間可由兩個長度為 \(2^{p}\) 的區間得出
    • \(f(i, p + 1) = \max(f(i, p), f(i + 2^p, p))\)
  • 這種作法每次會得到一個兩倍長的區間
  • 我們稱其為倍增法(binary lifting)
  • 從小做到大,用前面的結果算後面的

查詢與複雜度

  • 如何查詢?
    • 假設查詢區間為 \([l, r)\)
    • 令 \(d = \lfloor \log_2(r - l) \rfloor\)(中間元素的個數取 \(\log\))
    • 最大值 \(=\max(區間前\ d\ 個之 \max, 區間後\ d\ 個之 \max)\)
    • 即 \(\displaystyle \max_{i \in [l, r)} a_i = \max(f(l, d), f(r - 2^d, d))\)
  • 複雜度
    • 建表:每層 \(\mathcal O(n)\) 個元素,\(\mathcal O(\log n)\) 層,總共 \(\mathcal O(n \log n)\)
    • 查詢:動作數量是常數,\(\mathcal O(1)\)
    • 總複雜度 \(\mathcal O(n \log n + q)\)

Sparse Table Code

struct SparseTable {
    vector< vector<int> > v;
    SparseTable(int N): v(__lg(N) + 1, vector<int>(N)) {
        for (auto& n: v[0]) cin >> n;
        for (int i = 0, p = 1; i < __lg(N); i++, p <<= 1) {
            for (int j = 0; j + p < N; j++) {
                v[i + 1][j] = max(v[i][j], v[i][j + p]);
            }
        }
    }

    int query(int l, int r) {
        int d = __lg(r - l);
        return max(v[d][l], v[d][r - (1 << d)]);
    }
};

題目

Segment Tree

線段樹

複習一下

  • 線段樹是什麼
    • 可以做到「點修改」、「區間查詢」之資料結構
      • 修改和查詢的時間複雜度皆為 \(\mathcal O(\log n)\)
    • 基本的修改能做到「加值」、「指定值」等
    • 查詢能夠做到「極值查詢」、「總和查詢」等
  • 作法呢?
    • 存的方法有「陣列式」和「指標式」
    • 陣列式的操作可以用「遞迴式」或「迭代式」進行
  • 之前的課程以陣列式儲存,遞迴式操作為主
  • 本日除了遞迴式還會稍微介紹一下迭代式

線段樹模擬器

  • 遞迴式
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0

線段樹模擬器

  • 遞迴式
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
33

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

線段樹模擬器

  • 遞迴式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
20
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
22
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
27
[0, 8)
1
51

Recursive Segment Tree

  • \(\mathcal O(n)\) 初始化
#define lc(i) (i << 1)
#define rc(i) (i << 1 ^ 1)

typedef long long ll;

struct SegmentTree {
    vector<ll> v;

    SegmentTree(int N): v(4 << __lg(N)) {
    	int L = 2 << __lg(N);
        for (int i = L; i < L + N; i++) cin >> v[i];
        for (int i = L - 1; i > 0; i--) {
            v[i] = v[lc(i)] + v[rc(i)];
        }
    }

    void modify(int mi, ll d, int l, int r, int i = 1) {
        if (mi <= l && r <= mi) {
            v[i] += d;
            return;
        }

        int m = l + r >> 1;

        if (mi < m) modify(mi, d, l, m, lc(i));
        if (m < mi) modify(mi, d, m, r, rc(i));
        
        v[i] = v[lc(i)] + v[rc(i)];
    }

    ll query(int ql, int qr, int l, int r, int i = 1) {
        if (ql <= l && r <= qr) return v[i];

        int m = l + r >> 1;
        ll ans = 0;

        if (ql < m) ans += query(ql, qr, l, m, lc(i));
        if (m < qr) ans += query(ql, qr, m, r, rc(i));

        return ans;
    }
};

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
8
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
15
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
24
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
33

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

線段樹模擬器

  • 迭代式
[0, 1)
8
2
[1, 2)
9
7
[2, 3)
10
2
[3, 4)
11
7
[4, 5)
12
2
[5, 6)
13
2
[6, 7)
14
5
[7, 8)
15
0
[0, 2)
4
9
[2, 4)
5
9
[4, 6)
6
4
[6, 8)
7
5
[0, 4)
2
18
[4, 8)
3
9
[0, 8)
1
27

Iterative Segment Tree

  • 快,很快,非常快
#define lc(i) (i<<1)
#define rc(i) (i<<1^1)

typedef long long ll;

struct SegmentTree {
    int L;
    vector<ll> v;

    SegmentTree(int N): L(N), v(L << 1) {
        for (int i = N; i < (N << 1); i++) cin >> v[i];

        for (int i = N - 1; i > 0; i--) {
            v[i] = v[lc(i)] + v[rc(i)];
        }
    }

    void modify(int i, ll d) {
        for (i += L; i; i >>= 1) v[i] += d;
    }

    ll query(int l, int r) {
        ll sum = 0;
        for (l += L, r += L; l < r; l >>= 1, r >>= 1) {
            if (l & 1) sum += v[l++];
            if (r & 1) sum += v[--r];
        }
        return sum;
    }
};

等等,為什麼

  • 仔細看會發現,迭代式線段樹只要開到 \(2n\) 的空間?
  • 為什麼這是好的?
    • 這樣會變成很多棵小的完美二元樹,詳細證明我不會
    • reference
    • 如果覺得很怪就還是開 \(4n\) 或 \(4 \times 2^{\lfloor \log_2 n \rfloor}\)

線段樹暖身題

Lazy Propagation

懶標

區間操作與區間和

  • 用我們學到的東西,區間修改與區間求和貌似無法同時做出來?
    • 點修改與區間和:BIT / 線段樹
    • 區間修改與點值:(BIT / 線段樹)+ 差分 & 前綴和
    • 點修改與點值:那個叫陣列
  • 有可能做到區間修改與區間和同時嗎?
    • 可以,用兩個 BIT 來維護!
    • reference
  • 但 BIT 很難做區間極值
    • 有沒有有更一般化的方法?
    • 修改的時候別那麼急,懶懶的慢慢來!

區間修改

  • 先想像一下,要怎麼做區間修改
    • 跟查詢類似的方法
    • 發現當前節點之 \(l, r\) 在要修改的區間 \(L, R\) 中就修改
      • 這會有什麼問題?
      • \((L, R) = (0, n)\) 的時要修改 \(\mathcal O(n \log n)\) 個節點,爛透了
    • 不如這樣,有必要改的再改

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
0
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
0
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
0
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
0
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
0
[5, 6)
13
0
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
0
0
0
0
0
0
4
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
4
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
4
4
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
8
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
0
[4, 8)
3
8
[0, 8)
1
8
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
8
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
0
[3, 4)
11
0
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
0
0
0
0
0
3
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
3
[3, 4)
11
3
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
3
3
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
3
[3, 4)
11
3
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
3
3
0
0
0
0
0
0

懶標模擬器

  • 遞迴式
  • 藍色的是尚未往下傳的更新標記
[0, 1)
8
0
[1, 2)
9
0
[2, 3)
10
3
[3, 4)
11
3
[4, 5)
12
7
[5, 6)
13
4
[6, 7)
14
0
[7, 8)
15
0
[0, 2)
4
0
[2, 4)
5
6
[4, 6)
6
11
[6, 8)
7
0
[0, 4)
2
6
[4, 8)
3
11
[0, 8)
1
19
0
0
0
0
0
4
7
3
3
0
0
0
0
0
0

Recursive Code

#define lc(i) (i << 1)
#define rc(i) (i << 1 ^ 1)

using namespace std;

typedef long long ll;

struct SegmentTree {
    int L;
    vector<ll> v, tag;

    SegmentTree(int N): L(2 << __lg(N)), v(L << 1), tag(L << 1) {
        for (int i = L; i < L + N; i++) cin >> v[i];
        for (int i = L - 1; i > 0; i--) {
            v[i] = v[lc(i)] + v[rc(i)];
        }
    }

    void push(int l, int r, int i) {
        int m = l + r >> 1;
        tag[lc(i)] += tag[i];
        tag[rc(i)] += tag[i];
        v[lc(i)] += (m - l) * tag[i];
        v[rc(i)] += (r - m) * tag[i];
        tag[i] = 0;
    }

    void pull(int i) {
        v[i] = v[lc(i)] + v[rc(i)];
    }

    void modify(int ml, int mr, ll d, int l, int r, int i = 1) {
        if (ml <= l && r <= mr) {
            v[i] += (r - l) * d;
            tag[i] += d;
            return;
        }

        int m = l + r >> 1;

        push(l, r, i);
        if (ml < m) modify(ml, mr, d, l, m, lc(i));
        if (m < mr) modify(ml, mr, d, m, r, rc(i));
        pull(i);
    }

    ll query(int ql, int qr, int l, int r, int i = 1) {
        if (ql <= l && r <= qr) return v[i];

        int m = l + r >> 1;
        ll ans = 0;

        push(l, r, i);
        if (ql < m) ans += query(ql, qr, l, m, lc(i));
        if (m < qr) ans += query(ql, qr, m, r, rc(i));
        pull(i);

        return ans;
    }
};

Iterative Code

#define lc(i) (i<<1)
#define rc(i) (i<<1^1)

typedef long long ll;

struct SegmentTree {
    int L;
    vector<ll> v, sz, tag;

    SegmentTree(int N): L(N), v(L << 1), sz(L << 1, 1), tag(L << 1) {
        for (int i = N; i < (N << 1); i++) cin >> v[i];

        for (int i = N - 1; i > 0; i--) {
            v[i] = v[lc(i)] + v[rc(i)];
            sz[i] = sz[lc(i)] + sz[rc(i)];
        }
    }

    void update(int i, ll d) {
        v[i] += d * sz[i];
        tag[i] += d;
    }

    void pull(int i) {
        for (int h = __lg(L) + 1; h; h--) {
            int p = i >> h;
            if (tag[p]) {
                update(lc(p), tag[p]);
                update(rc(p), tag[p]);
                tag[p] = 0;
            }
        }
    }

    void push(int i) {
        for (i >>= 1; i; i >>= 1) {
            v[i] = v[lc(i)] + v[rc(i)] + tag[i] * sz[i];
        }
    }

    void modify(int l, int r, ll d) {
        int ll = l + L, rr = r + L - 1;
        pull(ll), pull(rr);
        for (l += L, r += L; l < r; l >>= 1, r >>= 1) {
            if (l & 1) update(l++, d);
            if (r & 1) update(--r, d);
        }
        push(ll), push(rr);
    }

    ll query(int l, int r) {
        ll sum = 0;
        pull(l + L), pull(r + L - 1);
        for (l += L, r += L; l < r; l >>= 1, r >>= 1) {
            if (l & 1) sum += v[l++];
            if (r & 1) sum += v[--r];
        }
        return sum;
    }
};

例題

沒了

謝謝大家

建北電資 27th 演算法[15] 資料結構[1]

By Brine

建北電資 27th 演算法[15] 資料結構[1]

演算法[15] 稀疏表、懶標線段樹

  • 340