數據結構
Ice-cream Tycoon
平衡樹 / 線段樹二分。
對于平衡樹而言, 構造一個函數, 求出拿到最便宜的所需數量的 ice-cream 的價格(利用類似于樹上查排名的操作即可), 比較該價格與所有錢數的差別即可。
(資料圖)
對于線段樹二分而言, 利用 數量 的單調性, 求出對應的節點, 然后修改該節點前的所有被影響到的節點即可, 或許用 zkw 線段樹更為方便。
兩者單次詢問時間復雜度均為 \(O(\log n)\), 需要注意的是, FHQ 線段樹的實現是注意分裂了 就要 合并。
New Year Tree
dfs 序 + 線段樹。
利用 dfs 序維護 與 LCA 和 子樹 有關的問題比較常見(博客概述)。
由于顏色數小于 60, 那么就用線段樹維護區間異或和即可。 時間復雜度 為 \(O(n \log n)\)。
需要注意, builtin_popcount 針對的是 int 型整數, 無法計算 long long 類型的數。
Ping-Pong
并查集 + 線段樹。
首先, 我們意識到兩個區間能否到達是 一條單向邊, 且兩個區間間的關系只有三種情況。
對于兩個可以互達的區間, 顯然, 我們可以將他們合并為 一個大區間, 大區間的端點為 \(\min(x_1, x_2), \max(y_1, y_2)\), 我們可以用 并查集 來表示這種關系。
怎樣判斷兩個區間是否有這樣的關系 ?
考慮到每個區間的關系只與相對位置有關, 我們先將所有點離散化。 那么此時, 最多的點的個數為 \(2 \times 10^5\)。 我們完全可以利用一個 vector 存儲每個點所在的區間。 考慮到區間的長度一定單調遞增, 那么新增的區間一定不可能被其他區間所包含, 那么此時, 它與之前的區間要么可以互達, 要么只能由其他區間到達它, 要么毫無聯系, 此時, 求與它能夠互達的區間實際上是求它的兩個端點處被多少區間經過。
再想到如果一個點一個點地打標記, 時間復雜度過高。 我們可以利用線段樹, 給一定的區間打標記, 而不下傳到每個子節點, 使修改的時間復雜度達到 \(O(\log n)\)。
最終的詢問即判斷兩個區間是否在同一個集合中 或者 兩個集合是否互相包含。
Life as a Monster
平衡樹 + 數學。
對于一個格點到另一個格點的距離, 由于可以斜向走, 故而我們所求的實際上是 切比雪夫距離。
我們記 切比雪夫距離為 \(D<(x_1, y_1), (x_2, y_2)>\) = \(min(|x_1 - x_2|, |y_1 - y_2|)\), 由于對于一個數 x 的絕對值 \(|x| = \min(x, -x)\), 那么此時, \(D<(x_1, y_1), (x_2, y_2)> = \min(x_1 - x_2, x_2 - x_1, y_1 - y_2, y_2 - y_1)\)
我們知道兩個點間的 曼哈頓距離 \(d<(x_1, y_1), (x_2, y_2)>\) = \(|x_1 - x_2| + |y_1 - y_2|\)= \(\max(x_1 - x_2, x_2 - x_1) + \max(y_1 - y_2, y_2 - y_1)\) = \(\max(x_1 - x_2 + y_ 1 - y_2, x_1 - x_2 + y_2 - y_1, x_2 - x_1 + y_1 - y_2, x_2 - x_1 + y_2 - y_1)\)(兩者交叉相加)。
此時, 倘若我們令 任意點 \((x, y) =>(x + y, x - y)\), 我們發現 :
\(x_1 - x_2 + y_ 1 - y_2\) = \((x_1 + y_1) - (x_2 + y_2) + (x_1 - y_1) - (x_2 - y_2)\) = \(2 \times (x_1 - x_2)\)。
同理, 我們可以得到 :
\(d<(x_1, y_1), (x_2, y_2)>\) = \(\max(2x_1 - 2x_2, 2y_1 - 2y_2, 2y_2 - 2y_1, 2x_2 - 2x_1)\)。
于是, 我們有一個結論, 任意兩格點間的 切比雪夫距離 是 這兩個格點通過 \((x, y) => (x + y, x - y)\) 轉化后的曼哈頓距離的 一半。
因此, 我們可以將所有點轉化為 \((x, y) => (x + y, x - y)\) 意義下的點, 然后求任意一點到 n 個點的曼哈頓距離。 考慮到曼哈頓距離中的 x 和 y 沒有關聯, 而唯一限制是 絕對值, 我們可以利用兩顆平衡樹 分別維護 x 和 y 的值, 每次計算時查詢小于 給定值的 數的個數即可, 然后分別計算答案即可。
Tourists
圓方樹 + 樹鏈剖分。
因為要求不能重復經過一個點, 故而一個點雙聯通分量中的點可以互達。 于是想到把圖中的點雙縮點,維護圓方樹,把方點的值設為它周圍的圓點中點權最小的點的點權。 通過樹鏈剖分的方式求解路徑上的最小值。
對于修改操作, 每次維護方點即可, 考慮對每個方點建立 multiset 儲存每個方點周圍的原點的權值, 每次操作修改對應的方點即可。
此外, 每次修改時可以僅修改圓點的父節點(方點), 而不是修改與圓點相連的所有方點。 我們考慮每次修改一個圓點, 如果圓點位于葉子節點上, 此時只有一個方點與它相連,故而修改父親節點即可; 如果圓點不在葉子節點上, 此時可以發現, 如果一個路徑會用到該圓點的信息, 那么該路徑一定會經過該圓點。 故而這樣的做法不會影響正確性。
New Year and Conference
線段樹優化 。
考慮到我們選擇活動時 , 最少只需要選擇兩個 , 倘如我們選擇更多的活動 , 如果此時沖突 , 那我們一定能分離出兩個相沖突的活動 。
這樣 , 我們實際上是求是否存在兩個活動 , 使其在一個會場沖突 , 另一個不沖突 , 暴力時間復雜度 \(O(n^2)\) 。
我們考慮優化 。 我們先將活動分別按照 第一會場 和 第二會場經行時間排序 , 那么此時 , 一個活動可以被舉行 當且僅當 它與之前的活動在兩個會場之一沒有沖突 。
此時 , 問題就被轉化為 :
查詢滿足 \(r_j \ge r_i\) 中,\(x_j\) 的最大值 。 如果最大值比 \(y_i\) 大,那么就直接不合法 。
查詢滿足 \(r_j \ge r_i\) 中,\(y_j\) 的最小值 。 如果最小值比 \(x_i\) 小,那么直接不合法 。
故此 , 我們可以用線段樹優化這個過程 , 以一個會場的活動舉辦時間為軸 , 維護另一個會場的活動舉辦時間 , 總時間復雜度為 \(O(n \log n)\) 。
Tree Generator?
dfs序 + 線段樹。 類似的
這個題應該很容易聯想到 dfs序 維護直徑的方法, 但題意的轉化較難。
關鍵在于將 左括號 賦值為 \(1\), 右括號 賦值為 \(-1\)。 我們若將 左括號 看成向下走一步, 右括號 看成向上走一步, 那么, 對于任意一個點而言, 該節點的深度是 該節點 到 括號序列最左端之間, 右括號的數目 減去 左括號的數目,也就是該節點左側的 {1, -1} 序列的加和。
當我們知道每個節點的相對深度之后, 由于樹上任意兩點 \(u, v\) 間的距離為 \(dep_u + dep_v - 2 * dep_{lca}\), 因此我們可以仿照 dfs序 + 線段樹的方式, 維護樹上任意兩點間的最大距離, 即我們所要求的 直徑。
Roadside Trees
線段樹 + Dp
對于每棵樹的高度, 令 T 為此時時刻, t 為種植時間, 那么此時樹的高度為 \(h + T - t\) 。 由于我們只關心樹的相對高度, 因此我們可以假設每棵樹的高度為 \(h - t\) 恒定不變。
考慮種樹操作, 每次在一個位置種一棵樹。 由于每次種樹的高度不超過 10, 且每個樹的高度不同, 故最多只有 10 棵樹比現在這棵樹矮。考慮砍樹操作, 在一個位置種一棵樹, 下標小于 10。
gg..
數學
Strange Limit
給定 \(p\) 和 \(m\), 令 \(a_1 = p, a_{n + 1} = p^{a_n}\), 求 \(\lim\limits_{n \rightarrow + \infty}(a_n \mod m!)\) 。
我們知道, \(a = p^{p^{p^{.^{.^{.}}}}}\)。 考慮歐拉定理 : 如果 \(a\) 和 \(n\) 互質, 則有 \(a^{\phi(n)} \equiv 1 (\bmod n)\)。 那么對于任意的 $a, b, n, $ 有 \(a^b \equiv a^{\phi(n) + b \mod \phi(n)} (\bmod n)\)(拓展歐拉定理)。
對于本題, 由于 b 趨近于無窮, 那么 \(b \mod \phi(n)\) 趨近于 \(\phi(n)\), 我們可以不斷 遞歸, 由于 \(\phi(n)\) 的值不斷減小, 當 \(\phi(n)\)遞歸到 1 時, 就可以返回。
GCD Determinant
結論題, 詳見。
圖論
B:Fairy
我們知道, 一個圖是二分圖當這個圖中不存在奇環。
這意味著我們要刪除的邊要將圖中的所有奇環破壞掉(如果存在)。 當存在多個奇環時, 我們選擇的邊所有奇環的并集。
gg..
D:Connecting Cities
首先有一個不得不做的轉化, 將 \(|i - j| \times D + a_i + a_j\) 看成 \(\min((a_i + i \times D) + (a_j - j \times D) , (a_i - i \times D) + (a_j + j \times D))\)。
這樣的話我們可以通過維護 \(a_i + i \times D\) 和 \(a_i - i \times D\) 來得到我們要求的式子的結果, 反正比 帶個絕對值的式子好維護。
然后由于我們實際上要求一棵最小生成樹, 那么可以從 kruskal 和 Prim 兩種常見的最小生成樹算法考慮。
對于 kruskal 而言, 如果直接暴力建圖的話會有 \(n ^ 2\) 條邊。 考慮到有一些邊是一定不會被 kruskal 算法選擇的, 那么可以考慮優化建圖。 這里我的建圖方式來自于 lemondinosaur。 我們可以考慮分治, 由于我們每次將序列分成兩塊, 兩塊間有明顯的左右關系, 我們令 左塊中元素的編號為 i , 右塊中元素的編號為 j , 那么 兩者間邊的權值為 \(a_i - i \times D + a_j + j \times D\)。
我們可以在 左塊中 找到 最小 的 \(a_i - i \times D\) , 將該點與 右塊中的所有的點連邊 ; 在 右塊中 找到 最小 的 \(a_i + i \times D\), 將該點與左塊中 的所有點連邊, 相當于在兩個塊中找到最優點來 代替 兩個塊所有元素互相連邊。 由于我們要找的是最小的 邊 使得兩個塊聯通, 所以這種方式一定是最優解。 最后跑一邊 kruskal 即可。 總建邊數為 \(n \times \log n\), 總時間復雜度為 \(O(n \log^2n)\)
對于 Prim 而言, 我們考慮它的暴力流程, 發現實際上我們需要維護的是最小的邊權和 最小邊權是由 哪個 未被連接的點和 已連接的點組成的。
線段樹維護的思路來自于 200815147。 我們還是將所求式子的絕對值拆開, 通過線段樹來維護邊權的最小值, 再記錄一個標記, 表示組成最小邊的 點的標號。 那么現在的問題是 Prim 要求我們選擇的兩個點, 一個位于 已經被選擇的點集中, 另一個位于 還沒有被選擇的點集中。
我們考慮利用兩類數組來區分 這兩種點, 當一個點被選中時, 將其對應的一類數組清空, 另一類數組賦值。 每次計算兩個點間的距離時, 只用這兩種數組交錯而形成的邊, 這樣線段樹維護的邊權始終是 未被選擇的點 和 已被選擇的點間的距離。 總時間復雜度為 \(O(n \log n)\)
似乎這道題還有 模擬 Boruvka 算法的, 這里, 用 樹狀數組解題。
總的來說, 這道題還是比較好的, 每一種 最小生成樹 的算法都可以解題, 而每種解題方式都 有共同點, 也有各自的特色。
E:Tournament
如果一位選手在任意一個項目上可以打敗對手, 我們即從這位選手 向 他的對手連一條邊。 這樣會形成很多個有向環, 于是考慮縮點, 在所形成的 DAG 上, 入度為 0 的縮點 中包含的點的數目即為所求。
F:Case of Computer Network
對于一個 E-BCC,我們總可以給其內部的邊安排一個定向方式,使得其任何一個點都可以到達另外所有點。即 E-BCC 一定可以定向為 SCC。
我們可以考慮邊雙縮點得到一棵樹, 那么 s 到 t 的路徑是唯一且固定的, 即這些邊的定向已經確定。 倘若存在一條邊的定向矛盾, 即可判斷無解。
可以通過 LCA + 差分 的方式, 將對邊的標記轉化為對點的標記, 用兩個差分數組分別記錄從該點向上走 還是 向下走, 當一個點同時被標記時判斷無解。 時間復雜度 \(O(m + (n + q) \log(n))\)。
G:Gift
暴力做法 :先對每個點按照 \(g_i\) 排序, 然后從小到大依次枚舉 邊, 加入所有比當前邊 g 值小的邊, 按照 s 值排序后 跑一遍 kruskal 即可, 時間復雜度為 \(O(mn \log(n))\)。
實際上, 我們可以暴力的維護加入邊 s 值單調遞增, 通過類似于 插入排序 的方式 \(O(n)\) 地維護, 總時間復雜度 \(O(nm)\)。
H:BerDonalds
test2023.1.13 water
I:Commuter Pass
考慮將有向圖拆成無向圖, 存在 \(stDAG\) 的任何完整路徑都是 \(s - t\) 最短路。
答案有三種可能 :
不經過 \(s - t\) 最短路, \(ans = dis(u, v)\)。
\(u\) 從 \(x\) 接入 \(stDAG\), 從 \(y\) 離開 \(stDAG\) 前往 \(v\), \(ans = dis(u, x) + dis(y, v)\)。
\(v\) 從 \(x\) 接入 \(stDAG\), 從 \(y\) 離開 \(stDAG\) 前往 \(u\), \(ans = dis(v, x) + dis(y, u)\)。
我們可以先對 \(u, v\) 跑單源最短路, 預處理 \(dis(u, ?)\) 和 \(dis(v, ?)\)。
如何找到最小的 \(x, y\) 使 \(ans\) 最小? 考慮在 \(stDAG\) 上的 \(DP\)。
我們令 \(dpu_i\) 表示 \(s - i\) 路徑上最小的 \(dis(u, i)\), \(dpv_i\) 表示 \(s - i\) 路徑上最小的 \(dis(v, i)\)。
那么當我們將 \(i\) 視為 \(y\) 時, \(ans = \min(dpu(i) + dis(u, i), dpu(i) + dis(v, i))\)。
故而, 在求出 dpu 和 dpv 之后, 我們可以直接枚舉 i 得到答案。
對于正邊權圖,只要維護 vis 使得每個點只會被拿出來一次,Dijkstra 拿出來的順序就是在單源最短路 DAG 上的拓樸排序。
我們可以從 s 做一遍 dijkstra, 得出 dpu 和 dpv。