---
# System prepended metadata

title: Algorithm

---

# Algorithm 
[![hackmd-github-sync-badge](https://hackmd.io/kImG7w0vRJOIOhv2DBA6xQ/badge)](https://hackmd.io/kImG7w0vRJOIOhv2DBA6xQ)

- [segment tree](#segmentTree)
    * 前言
    * segTree基本結構
        * StructSEG
        * query
        * update node
        * lazy tag
    * 動態開點
        
- [BIT](#BIT)

- [convex Hull](#convexHull)


<h2 id="segmentTree">segmentTree</h2>

*   前言 (記憶體換時間)
    segment tree的作用：以低時間複雜度 解決(區間,交點)問題
    * 我們先嘗試以傳統的方式解決區間問題
        給定一個數列 n => [1 , 2 , 3 , 5 , -1 , 3 , 0] 並給定 n^2組index=>( i , j ) 
        試問 每組的n[i] ~ n[j] 最小數值為多少 ？ 
        假設我們不做任何優化 每次都需把 n[i]~n[j]掃一遍 掃t組 time complexity = O(n^3)
        ![](https://i.imgur.com/jLnGCK1.png)

    *   那嘗試一下DP的思維 我們花O(n^2)的時間把它建成表  
        我們知道min(n[0] ~ n[0])=1，那我們可由min(n[0] ~ n[0])推測 min(n[0] ~ n[1]) = 1
        所以只要是相鄰的格子都只需要O(1)時間複雜度 去獲得區間最小值。
        ![](https://i.imgur.com/8Vw9tkh.png)
        
        其實線段樹的精髓也在記錄，但這次是把DP表改成一棵樹
* segment tree 結構：
    ![](https://i.imgur.com/YuEqJ4I.png)
    *   原理(StructSEG)
        由[0,5]做二元樹把他們的區間分開，然後在遞迴回來的時候去紀錄value
        建樹時間複雜度為 O(n) , 獲取區間為 O(log(n))
        *   Struct segTree code timeComplexity = O(n) 
        ```c++
        void StructSEG(int l , int r , ll node){
            if(l==r) seg[node] = v[r] ; 
            else{
                int mid = (l+r) >> 1 , tmp = node << 1;
                StructSEG(l,mid,tmp) ; 
                StructSEG(mid+1,r,tmp+1) ;
                seg[node] = seg[tmp] + seg[tmp+1] ; 
            }
        }
        ```
    * query
        * 每次去二分搜找區間 並組合 timeComplexity = log(n) 
        * query code
        ```c++
        int query(int tL , int tR , int nL , int nR , int node){
            int mid = (nL+nR)>>1 , ans = 0 ; 
            if(tL <= nL && nR <= tR) return seg[node] ;
            if(tL <= mid) ans += query(tL,tR,nL   ,mid,(node<<1)  );
            if(tR  > mid) ans += query(tL,tR,mid+1,nR ,(node<<1)+1);  
            return ans ; 
        }
        ```
    * updateNode
        * 更新節點 timeComplexity = log(n)
        * code 
        ```c++
        void updateNode(int idx , int val , int l , int r , int node){
            if(l == r){
                seg[node] = val ;
                v[idx] = val ; 
            }
            else{
                int m = (l + r) >> 1 ; 
                int leftNode  = (node << 1) ; 
                int rightNode = (node << 1) + 1 ; 
                if(idx <= m && idx >= l) updateNode(idx , val , l , m , leftNode ) ;
                else updateNode(idx , val , m+1,r , rightNode ) ; 
                seg[node] = seg[leftNode] + seg[rightNode] ; 
            } 
        }
        ```
        * lazy tag 
          簡介：即使updateNode 也不需要即時全部走過 除非你要走這條路徑時才會將node更新

        **當cases = 點^2 , DP的解法會快一些
        線段樹建樹要O(nlogn) + 找點 O(n^2(log(n))) 但DP只要 建表O(n^2) + 找對O(n^2)
        所以可以加一個前置判斷 當 尋找次數 > 點的個數^2 就直接改用DP來做優化。**
        但不太可能拿來優化 , 除非空間給超過 x 
        ```
        建表log(n) + 尋找((x^2)*log(n)) = ((x^2)+1)*log(x) 
        x => ((x^2)+1)*log(x) = 2*10^9 
        x = 14450 
        ```
        記憶體空間必須給超過208802500 ，基本上可以斷定他在競賽中不可優化了
        ■ ︿ ■

<h2 id="BIT">BIT</h2>

<h2 id="convexHull">convexHull</h2>


```cpp=
typedef long long ll;
const int N = 5e5 + 5;
ll v[N], seg[N<<2];

void StructSEG(int l , int r , int node){
    if(l==r) seg[node] = v[r] ; 
    else{
        int mid = (l+r) >> 1 ;
        StructSEG(l,mid,node<<1) ; 
        StructSEG(mid+1,r,node << 1 | 1) ;
        seg[node] = seg[node << 1] + seg[ node << 1 | 1] ; 
    }
}

ll query(int tL , int tR , int nL , int nR , int node){
    if(tL > nR || tR < nL) return 0 ;
    ll mid = (nL+nR)>>1 , ans = 0;
    if(tL <= nL && nR <= tR) return seg[node] ;
    if(tL <= mid) ans += query(tL,tR,nL   ,mid,(node<<1));
    if(tR > mid) ans += query(tL,tR,mid+1,nR ,(node<<1)+1);  
    return ans ; 
}

int main(){
    int n, q, l, r; 
    cin >> n >> q;
    for(int i = 1 ; i <= n ; i++) cin >> v[i] ; 
    StructSEG(1,n,1) ;
    while(q--){
        cin >> l >> r ; 
        cout << query(l,r,1,n,1) << '\n' ;   
    }
}
```