---
# System prepended metadata

title: Algo｜Segment Tree 線段樹
tags: [Algo]

---

# Segment Tree 線段樹
## 1.單點加值，區間查詢最大值
```clike=
#include <bits/stdc++.h>
using namespace std;

struct node{
    int l, r, data;     // 該區間的最大值
}T[40000];

void build(int id, int l, int r, int *data){
    T[id].l = l;
    T[id].r = r;
    if(l == r){
        T[id].data = data[l];
    }else{
        int mid = (l+r) >> 1;
        build(id*2+1,     l, mid, data);
        build(id*2+2, mid+1,   r, data);
        T[id].data = max(T[id*2+1].data, T[id*2+2].data);
    }
}

void add(int id, int x, int val){   // 單點加值 data[x] += val
    if(T[id].l == T[id].r){
        T[id].data += val;
    }else{
        int mid = (T[id].l+T[id].r) >> 1;
        if(x<=mid)  add(id*2+1, x, val);
        else        add(id*2+2, x, val);
        T[id].data = max(T[id*2+1].data, T[id*2+2].data);
    }
}

int query(int id, int l, int r){    // 區間查詢 maximum of data[l..r]
    if(l == T[id].l && r == T[id].r){
        return T[id].data;
    }else{
        int mid = (T[id].l+T[id].r) >> 1;
        if(r<=mid)      return query(id*2+1, l, r);
        else if(l>mid)  return query(id*2+2, l, r);
        else            return max(query(id*2+1, l, mid), query(id*2+2, mid+1, r)); 
    }
}

int main(void){
    int data[10] = {2,1,3,1,2,4,1,3,5,2};
    build(0, 0, 9, data);
    for(int i=0;i<10;i++)
        for(int j=i;j<10;j++)
            cout<<query(0,i,j)<<(j==9?'\n':' ');   // query data[i..j]
    add(0, 3, 2);       // data[3] += 2
}
```
## 2.區間加值，單點查詢數值
```clike=
#include <bits/stdc++.h>
using namespace std;

struct node{
    int l, r, data;   // 完整覆蓋當前區間的線段數量
}T[40000];

void build(int id, int l, int r){
    T[id].l = l;
    T[id].r = r;
    T[id].data = 0;
    if(l == r){

    }else{
        int mid = (l+r) >> 1;
        build(id*2+1,     l, mid);
        build(id*2+2, mid+1,   r);
    }
}

void add(int id, int l, int r, int val){    // 區間加值 data[l..r] += val
    if(l == T[id].l && r == T[id].r){
        T[id].data += val;
    }else{
        int mid = (T[id].l+T[id].r) >> 1;
        if(r<=mid)      add(id*2+1, l, r, val);
        else if(l>mid)  add(id*2+2, l, r, val);
        else            add(id*2+1, l, mid, val), add(id*2+2, mid+1, r, val);
    }
}

int query(int id, int x){                   // 單點查詢 data[x]
    if(T[id].l == T[id].r){
        return T[id].data;
    }else{
        int mid = (T[id].l+T[id].r) >> 1;
        if(x<=mid)  return T[id].data + query(id*2+1, x);
        else        return T[id].data + query(id*2+2, x);
    }
}

int main(void){
    build(0, 0, 9);
    
    add(0, 1, 4, 2);    // data[1..4] += 2
    for(int i=0;i<10;i++)   cout<<query(0,i)<<" ";   cout<<endl;
    add(0, 3, 7, 1);    // data[3..7] += 1
    for(int i=0;i<10;i++)   cout<<query(0,i)<<" ";   cout<<endl;
    add(0, 1, 9, 3);    // data[1..9] += 3
    for(int i=0;i<10;i++)   cout<<query(0,i)<<" ";   cout<<endl;
    add(0, 2, 5, -2);   // data[2..5] -= 2
    for(int i=0;i<10;i++)   cout<<query(0,i)<<" ";   cout<<endl;
}
```
## 3.區間修改 (加值/改值)，區間查詢最大值
- 每個線段樹上的區間，可能被加值，也可能被改值
- 每次遍歷線段樹時，保證走到的區間是正確的
    - 加值或是改值就暫時放在那個節點上
    - 用到的時候，處理改值和加值的 tag 並正確維護 data 值，並把 tag 往下推一層
- 往下推一層 `push 的設計` (精隨就是懶?)
    - (1)如果同時存在改值和加值，則先處理改值再處理加值
    - (2)若把改值推給後代，強迫後代的加值 tag 變成 0
    - (3)若把加值推給後代，只更新後代的加值 tag
```clike=
#include <bits/stdc++.h>
using namespace std;

// 區間修改 (加值/改值)，區間查詢 (max)
struct node{
    int chg1, chg2, data;   // chg1 改值 , chg2 加值
    int l, r;
}T[40000];

void build(int id, int l, int r, int *data){
    T[id].l = l;
    T[id].r = r;
    T[id].chg1 = 0;
    T[id].chg2 = 0;
    if(l == r){
        T[id].data = data[l];
    }else{
        int mid = (l+r) >> 1;
        build(id*2+1,     l, mid, data);
        build(id*2+2, mid+1,   r, data);
        T[id].data = max(T[id*2+1].data, T[id*2+2].data);
    }
}

void push(int id){                          // 神奇 lazy tag 函數
    if(T[id].l == T[id].r){
        T[id].data = T[id].chg2 + ( T[id].chg1 ? T[id].chg1 : T[id].data );
        T[id].chg1 = 0;
        T[id].chg2 = 0;
        return;
    }
    int ll = id*2+1, rr = id*2+2;
    if(T[id].chg1 != 0){
        T[id].data = T[id].chg1;
        T[ll].chg1 = T[id].chg1, T[ll].chg2 = 0;
        T[rr].chg1 = T[id].chg1, T[rr].chg2 = 0;
        T[id].chg1 = 0;
    }
    if(T[id].chg2 != 0){
        T[id].data += T[id].chg2;
        T[ll].chg2 += T[id].chg2;
        T[rr].chg2 += T[id].chg2;
        T[id].chg2 = 0;
    }
}

void add(int id, int l, int r, int val){    // 區間加值
    push(id);
    if(l == T[id].l && r == T[id].r){
        T[id].chg2 += val;
    }else{
        int mid = (T[id].l+T[id].r) >> 1;
        if(r<=mid)      add(id*2+1, l, r, val);
        else if(l>mid)  add(id*2+2, l, r, val);
        else            add(id*2+1, l, mid, val), add(id*2+2, mid+1, r, val);
        push(id*2+1);    // revised
        push(id*2+2);    // revised
        T[id].data = max(T[id*2+1].data, T[id*2+2].data);
    }
}

void chg(int id, int l, int r, int val){    // 區間改值
    push(id);
    if(l == T[id].l && r == T[id].r){
        T[id].chg1 = val;
    }else{
        int mid = (T[id].l+T[id].r) >> 1;
        if(r<=mid)      chg(id*2+1, l, r, val);
        else if(l>mid)  chg(id*2+2, l, r, val);
        else            chg(id*2+1, l, mid, val), chg(id*2+2, mid+1, r, val);
        push(id*2+1);    // revised
        push(id*2+2);    // revised
        T[id].data = max(T[id*2+1].data, T[id*2+2].data);
    }
}

int query(int id, int l, int r){            // 區間查詢
    push(id);
    if(l == T[id].l && r == T[id].r){
        return T[id].data;
    }else{
        int mid = (T[id].l+T[id].r) >> 1;
        if(r<=mid)      return query(id*2+1, l, r);
        else if(l>mid)  return query(id*2+2, l, r);
        else            return max(query(id*2+1,l,mid), query(id*2+2,mid+1,r));
    }
}

void show(int *data){
    for(int i=0;i<16;i++)   cout<<query(0, i, i)<<" "; cout<<endl;
}

int main(){
    int data[16] = {1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4};
    build(0, 0, 15, data);  show(data);
    add(0, 3, 8, 4);        show(data);
    chg(0, 1, 10, 9);       show(data);
    add(0, 4, 14, -2);      show(data);
    chg(0, 7, 12, 5);       show(data);
    add(0, 2, 13, -1);      show(data);
}
```

