# 111 選手班 - 綜合應用 ###### tags: `宜中資訊` `CP` 2022.08.26 ## 持久化線段樹 [110 slide](https://hackmd.io/@Ccucumber12/B1PqClBlY#/6) ### 問題 給定數列 $a_1, a_2, \cdots, a_N$,請支援以下操作 $Q$ 次 - $\text{add x v}$:將 $a_x$ 加上 $v$ - $\text{query l r k}$:詢問第 $k$ 次修改號的狀態(sum, max, ...) $N, Q, a_i \le 10^6$ ### 操作 #### 離線 Offline - 整理好需要那些詢問 - 正常操作過程中直接查詢那些詢問 - 最後再次排序輸出 - $\mathcal O(Q\log N)$ #### 在線 Online - 回到過去的某個版本 - 每次更新都重新開一顆線段樹 - 空間複雜度:$\mathcal O(QN)$ ### Copy on Write - 指備份被修改的點 $\mathcal O(\log N)$ - 每個版本用一個新的 root 指下去 - 空間複雜度:$\mathcal O(N + Q\log N)$ ![](https://i.imgur.com/8pd6T8H.png) ![](https://i.imgur.com/aZKqUMm.png) ### Code :::spoiler ```cpp= struct node{ node *lch, *rch; int val; node () { lch = rch = nullptr; val = 0; } void pull(){ val = lch -> val + rch -> val; // val = max(lch -> val, rch -> val) ; } }; void build(int l, int r, node *&p){ p = new node(); if(l == r) { p -> val = a[l] ; return ; } int mid = (l + r) >> 1; build(l, mid, p -> lch); build(mid+1, r, p -> rch); p -> pull() ; } void modify(int pos, int val, int lb, int rb, node *pre, node *&cur){ cur = new node(); if(lb == rb) { cur -> val = pre -> val + val; return ; } cur -> lch = pre -> lch; cur -> rch = pre -> rch; int mid = (lb + rb) >> 1; if(pos <= mid) modify(pos, lb, mid, pre -> lch, cur -> lch); if(mid < pos) modify(pos, mid+1, rb, pre -> rch, cur -> rch); cur -> pull(); } int query(int l, int r, int lb, int rb, node *p) { if(l <= lb && rb <= r) { return p -> val ; } int mid = (lb + rb) >> 1 ; if(l <= mid) query(l, r, lb, mid, p -> lch) ; if(mid < r) query(l, r, mid+1, rb, p -> rch) ; } int main() { node *ti[100010] ; build(1, n, ti[0]) ; for(int i=1; i<=Q; ++i) { int op ; cin >> op ; if(op == 1) { int x, v ; cin >> x >> v ; modify(x, v, 1, n, ti[i-1], ti[i]) ; } else { int l, r, k ; cin >> l >> r >> k ; cout << query(l, r, 1, n, ti[k]) << endl ; ti[i] = ti[i-1] } } } ``` ::: ## 值域線段樹 ### 問題:區間第 k 小 給定數列 $a_1, a_2, \cdots, a_N$,請支援以下操作 $Q$ 次 - $\text{query l r k}$:詢問區間 $a_l, \cdots, a_r$ 中第 $k$ 小的數字 $N, Q, a_i \le 10^6$ ### 值域線段樹 - 將 $\text{seg}[i]$ 定為數字 $i$ 有多少個 - 區間和 $\text{seg[L, R]}$ 代表介於 $[L, R]$ 區間的數字有多少個 **Case 1:只詢問 $[1, N]$** - 依序把每個數字塞進去線段樹 - 尋找最小的 $i$,使得區間和 $\text{seg}[1, i]$ 恰好大於等於 $k$ - 二分搜:$\mathcal O(\log N)$ **Case 2:只詢問 $[1, R]$** - $[1, R]$ 相當於塞到第 $R$ 個數字時線段樹的 Case 1 - 持久化線段樹查詢:第 $R$ 個版本 **General:詢問 $[L, R]$** - 詢問第 $L$ 到第 $R$ 個版本的數字 - 持久化線段樹查詢:版本 $R$ - 版本 $(L-1)$ ## 單調隊列優化 ### 題目 請支援以下 $Q$ 筆操作 - 插入一條直線:$f(x) = a_i(x) + b_i$ - 查詢目前直線中 $x_i$ 的最大值:$\max (f(x_i))$ 限制 - $Q \le 10^6$ - 對於直線有:$a_1 \ge a_2 \ge \cdots \ge a_n \ge 1$ - 對於查詢有:$1 \le x_1 \le x_2 \le \cdots \le x_n$ ### 單調性 - 斜率只會越來越小 - 查詢位置越來越大 ### 單調隊列優化 - 加入一條新的直線 - 可能會壓過某些之前的直線 - 那些直線再也不會用到了 - 往後查詢某個 $x$ - 可能會離開某些直線的最大值範圍 - 那些直線再也不會用到了 - 用 stack 維護目前跟之後有可能成為最大值的直線 - top 是目前 $x$ 最大值的位置 - 每次加入新的直線可能會 pop 掉某些人 - 每次往後移動 $x$ 也可能會 pop 掉某些人 ## 斜率優化 ### 題目 請支援以下 $Q$ 筆操作 - 插入一條直線:$f(x) = a_i(x) + b_i$ - 查詢目前直線中 $x_i$ 的最大值:$\max (f(x_i))$ 限制 - $Q \le 10^6$ ### 覆蓋區間 - 每個直線都的最大值區域必定是一個連續區間 - 用線段樹維護每個區間的最大值是被哪條直線覆蓋 - 每次插入直線都是一些區間修改 - 如果完整覆蓋 or 被完整覆蓋就結束遞迴 - 否則繼續往下遞迴 ![](https://i.imgur.com/MlIrYUA.png) ### 時間複雜度 - 每次必定只有其中一邊會往下遞迴(因為是線性函數) - 最多往下遞迴 $\log (N)$ 層 - 總時間複雜度 $\mathcal O(Q\log N)$ ## 李超線段樹 ### 題目 請支援以下 $Q$ 筆操作 - 插入一條線段:$f(x) = a_i(x) + b_i,\quad x\in [L, R]$ - 查詢目前直線中 $x_i$ 的最大值:$\max (f(x_i))$ ### 李超線段樹 - 把線段拆分成 $\log N$ 個完整覆蓋的線段 - 每個再各自往下遞迴修改 - 單次操作時間複雜度 $\mathcal O(\log^2 N)$ ## DP 優化應用 ### 題目 有 $N$ 個石頭,第 $i$ 個石頭的高度是 $h_i$,目標從第 $1$ 個石頭跳到第 $N$ 個石頭。若現在位於第 $j$ 個石頭,可以跳到任何一個編號大於 $j$ 的石頭 $i$,但需要花費 $(h_i - h_j)^2 + C$ 能量。請問最少需要花費少能量才能到達石頭 $N$? 限制 - $N \le 2\times 10^5$ - $C \le 10^12$ - $1 \le h_i \le 10^6$ ### DP - 定義:$\text{dp}[i]$ 為跳到石頭 $i$ 的最小花費 - 轉移:$\text{dp}[i] = \max\{(h_j - h_i)^2 + C + \text{dp}[j]\ |\ j < i\}$ - 時間複雜度:$\mathcal O(N^2)$ ### 把轉移拆開 - $(h_j - h_i)^2 + C + \text{dp}[j]$ - $h_j^2 -2h_ih_j + h_i^2 + C + \text{dp}[j]$ - $2h_j\cdot (h_i) + h_j^2 + \text{dp}[j] + h_i^2 + C$ - $\max\{2h_j\cdot(h_i) + h_j^2 + \text{dp}[j]\ |\ j < i\} + h_i^2 + C$ - $\max\{a_j\cdot(h_i) + b_j |\ j < i\} + h_i^2 + C$ - 斜率優化! - 時間複雜度:$\mathcal O(N\log N)$ ## 題單 - [contest](https://vjudge.net/contest/512270) - password:`111apcs`