本文**了線段樹的幾種模型和一些例題。
線段樹,是一類可以在對數複雜度內處理區間型問題的資料結構。
這是一張線段樹的圖。容易發現,線段樹是一棵二叉樹,每個節點儲存了這個節點管理的區間 \(l,r\)(黑色數字)這個區間的 \(\operatorname\)(紅色數字)(也可以是 \(\max,\min\) 等等)。
在圖中,我們發現了線段樹的一些特徵:乙個節點的兩個兒子節點分別管理這個節點的一半,乙個節點的 \(\operatorname\) 值等於兩個兒子節點的 \(\operatorname\) 值。通過這兩個特徵,就可以通過遞迴的方式構造一棵線段樹(應該都知道二叉樹的簡便構造方法吧)。
ci n = 1e5, n4 = 4e5; int sum[n4 + 5], l[n4 + 5], r[n4 + 5], arr[n + 5]; // 線段樹需要開四倍大小
void pushup (int root) // 線段樹子節點向父親節點更新
void build (int root, int l, int r) // 葉子節點
int mid = (l + r) >> 1; build (root << 1, l, mid); build (root << 1 | 1, mid + 1, r);
pushup (root); // 向上更新
}
還是上面那棵線段樹,如果我們需要查詢區間 \([1,7]\) 的和,一種做法當然是從 \([1,1]\) 加到 \([7,7]\)
(那你寫線段樹幹嘛)。但是我們發現,從 \([1,1]\) 到 \([4,4]\) 的和就是 \([1,4]\) 的 \(\operatorname\) 值。所以 \([1,7]\) 的和就等於 \([1,4],[5,6],[7,7]\) 的和。但是怎麼判斷查詢區間可以分成幾個小區間呢?考慮分類,我們從根節點向下遞迴(因為從上向下遞迴的,可以保證分割的區間數最少):如果當前區間包含在查詢區間內,那麼直接返回該節點 \(\operatorname\) 值;如果查詢區間與當前區間的左兒子有交集,向左兒子遞迴;如果右兒子有交集,向右兒子遞迴。
int sum (int root, int l, int r)
(比區間查詢簡單多了)只需要找到對應的葉子節點,在遞迴返回的時候向上更新就好了。
void change (int root, int x, int k) // 到達葉子節點
if (l[root] > x || r[root] < x) return ;
int mid = (l[root] + r[root]) >> 1;
if (x <= mid) change (root << 1, x, k);
else if (x > mid) change (root << 1 | 1, x, k);
pushup (root); // 向上更新
}
考慮換一種實現方式,我們構造一棵 \(\operatorname\) 值全為 \(0\) 的線段樹。區間修改時把包含的區間加上 \(k\) 的標記,單點查詢時把沿路的標記加起來,再加上這個點原本的值即可。
OI學習筆記
組合數遞迴求法 c n k c k c 卡特蘭數 cat n cat 0cat cat 1cat cat cat 1 cat cat 0 fracc n 手動開o2 pragma g optimize 2 費馬小定理 若p為質數,a p equiv a pmod p 可用於求逆元 尤拉定理 若a,n...
線段樹學習筆記
線段樹是一種 二叉搜尋樹 與區間樹 相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。使用線段樹可以快速的查詢某乙個節點在若干條線段中出現的次數,時間複雜度為o logn 而未優化的 空間複雜度 為2n,因此有時需要離散化讓空間壓縮。以下筆記摘自lcomyn神犇部落格 1....
線段樹學習筆記
本文筆記在參考一步一步理解線段樹 tenos的基礎上形成 線段樹,也是二叉搜尋樹的一種,是基於陣列,但是優於陣列的一種資料結構。同時結合預處理 時間複雜度一般在o n 使得從原來陣列的o n 的查詢和更新複雜度降到了o logn 在處理很大資料量的資料更新和查詢最值方面變得簡單,值得一提的是,它的構...