只做完了
場切的幾題
A - Advertisement Matching
\(N\) 個廣告,\(M\) 個人,第 \(i\) 個廣告需要分給 \(a_i\) 個人觀看,第 \(i\) 個人只能收到不超過 \(b_i\) 個不同的廣告。\(Q\) 次操作,每次選擇一個 \(a\) 或 \(b\) 增加或減少 \(1\),每次操作之后求問能否把所有的廣告都分配完。\(N,M,Q,a_i,b_i\le 2.5\times 10^5\)。
首先不難看出一個二分圖模型:第 \(i\) 個左部點需要匹配 \(a_i\) 個右部點,第 \(i\) 個右部點最大匹配 \(b_i\) 個左部點。然后判斷是否存在方案使所有左部點都匹配完。
接下來引入一個 Gale–Ryser 定理,在本題中可以寫成:先將 \(a\) 降序排序,那么若存在方案,當且僅當 \(\forall k\in[1, N]\) 都有 \(\sum_{i=1}^k a_i\le \sum_{i=1}^M \min(b_i,k)\) 成立。下面是證明:
先建立網絡流模型:源點向左邊連邊,容量 \(a\);右邊向匯點連 \(b\) 容量邊;左右部點之間都連一條 \(1\) 容量邊。現在需要證明最大流 \(\ge \sum_{i=1}^N a\),等價於最小割 \(\ge \sum_{i=1}^N a\)。
考慮一個割,源點這邊左部點記作 \(S\),右部點記作 \(T\)。那么最小割可以表示為:
\[\sum_{i\notin S} a_i+\sum_{i\in T} b_i + |S|\times(M-|T|) \]令 \(|S|=k\),那么將 \(a\) 降序排序后(記作 \(a'\))有:\(\min_{S}\{\sum_{i\notin S} a_i\} = \sum_{i=1}^k a'_i\)。而后兩個和式又可以寫成 \(\sum_{i\in T}b_i+\sum_{i\notin T}k\)。貪心地考慮,要使其最小化,那么一定將大於 \(k\) 的那些 \(b_i\) 換為 \(k\)。那么最終最小割為:
\[\sum_{i=1}^k a'_i+\sum_{i=1}^M\min(b_i,k) \]
然后就對於每個 \(k\),維護出 \(\sum_{i=1}^M \min(b_i,k)-\sum_{i=1}^k a_i\) 即可。這個 \(\min\) 非常惡心,考慮一個巧妙的轉化:設 \(c_k = \sum_{i=1}^M [b_i\ge k]\),即不小於 \(k\) 的 \(b\) 的個數。我們發現原式 \(=\sum_{i=1}^k (c_i-a_i)\),簡潔了許多,可以線段樹維護。那么怎么修改呢?
- 對於修改 \(b\),由於只有 \(1\) 的變化量,不難發現只需要修改 \(c\) 的一個位置就行;
- 對於 \(a\),我們可以在排好序的 \(a'\) 上找到第一個或最后一個對應值的位置(二分),然后直接在 \(a'\) 上改掉。由於變化量為 \(1\),不會改動原來 \(a'\) 的降序。
最后復雜度為 \(O(N\log N)\)。
C - Economic One-way Roads
給定一個 \(N\) 個點的無向圖,每條邊需要被定向,\(a_{i,j}\) 表示將 \(i,j\) 之間的邊定向為 \(i\to j\) 的費用。如果 \(a_{i,j}=-1\) 說明原圖中不存在連接 \(i,j\) 的邊。你需要用最少的總費用給原圖中的邊定向,使得整張有向圖強連通。\(N\le 18,a_{i,j}\in[-1, 10^6]\)。
首先需要知道,一張圖強連通,當且僅當可以通過以下方式構造:
維護一個點集 \(V\),首先任意選擇一個點 \(v\),加入 \(V\) 中。然后重復執行以下操作:
- 在 \(V\) 選出任意兩個點 \(x,y\)(\(x\) 可以等於 \(y\));
- 找出若干個 \(\notin V\) 的點 \(\{u_1, u_2, \cdots, u_k\}\)(可以不選任何的點),然后連接 \(x\to u_1 \to u_2 \to\cdots \to u_k \to y\),並將 \(\{u_1, u_2, \cdots, u_k\}\) 加入點集 \(V\)。
這種方法被成為“耳分解”,上面每次選出的那個路徑就是“耳”。
根據這個我們有一個朴素的狀壓 dp:\(f(S)\) 表示選出點集 \(S\) 構成強連通圖的最小選擇的總邊花費和。那么對於每個狀態 \(f(S)\),都可以枚舉一遍 \(S\) 補集的子集作為“耳”,然后還有 \(O(|S|^2)\) 種方法選擇“耳”的兩端。總復雜度為 \(O(3^N N^2)\)。
這樣的方法不僅需要優化,而且存在正確性的問題:可能存在一條邊正反都沒有記入答案,從而結果偏小。有一種巧妙的解決方法是:對於一條未定向的邊,兩個方向的花費分別為 \(a,b\)。這總得選一條吧?那么不妨先將 \(\min(a,b)\) 加入答案,然后把兩個方向分別用 \(a-\min(a,b)\),\(b-\min(a,b)\) 計算。這樣就能直接拆開了。
考慮優化:引入 \(g(S,u,v)\) 為當前構造的“耳”還需要一條連接 \(u,v\) 的路徑,強連通部分以及 \(u,v\) 的非完整“耳”構成的點集為 \(S\)。容易發現,記錄“耳”的端點的好處在於,可以直接從 \(u,v\) 開始拓展而不是盲目地枚舉集合。
復雜度可以做到不超過 \(O(2^N N^3)\),當然實現轉移還是有及其多的細節的。
D - Just Meeting
求構造一個 \(N\times N\) 的矩陣 \(C\),滿足 \(\forall i\in [1, n]\),都滿足 \(\forall\) 不同於 \(i\) 的 \(j,k(j\ne k)\),有 \(C_{i,j}\ge \min(C_{i,k},C_{k,j})\)。並且 \(C_{i,j}=C_{j,i}\)。現有 \(M\) 個位置已經有固定值,除這些位置外都可以任意選擇數字。求所有可行構造方案中 \(\sum_{i=1}^n\sum_{j=i+1}^nC_{i,j}\) 的最小值。\(N,M\le 3\times 10^5\),方案中的 \(C_{i,j}\in [1, 10^7]\)。
如果將 \(C\) 矩陣看做圖的鄰接矩陣的話,那么相當於要構造一個 \(N\) 個點的完全圖,滿足 \(\forall i\ne j\),不存在一個 \(k\) 使 \(C_{i,j}\le \min(C_{i,k}, C_{k,j})\)。也就是說 \(i,j\) 間不存在一條長為 \(2\) 的路徑其上最小比邊權小於 \(i,j\) 之間的邊。
考慮一個三元組 \((i,j,k)\),要使之滿足條件,不得存在一條邊嚴格小於其他兩條邊。那么小小討論一下:
- 三點間不存在邊:要求最小化 \(C\) 的總和必然是三個 \(1\) 啦;
- 只存在一條邊:另外兩條也是 \(1\);
- 存在兩條:由於新加入的邊不能作為最小的,原來兩條中最小的也不能讓它一個最小,於是新邊權必然為原來兩條中的較小者;
- 存在三條:現在就由不得選了,反而應該檢查是否合法。
那么拓展到多個點的情況,可以重復以上加邊操作,直到補成完全圖。可以證明不會因為補上的邊突然違反約束。
不難發現,倘若我們先求出一個原圖的最大生成森林,對於一條未補上的邊 \((i,j)\),樹上路徑的最小值(原圖的瓶頸路)就可以作為這條新邊的權值。對於原來存在但不屬於最大生成森林的邊,其邊權應該與樹上路徑的最小值相等:如果小了那么它會成為某個三元環的唯一最小值,大了又會導致路徑上那條最小邊成為唯一最小值。
最后實現的話,掏出一個 Kruskal 重構樹就沒了。復雜度 \(O(N\log N)\)。
E - Chemistry
給定一個 \(N\) 個點 \(M\) 條邊的無向圖,求有幾個區間 \([l,r](1\le l\le r\le n)\),滿足僅保留編號 \(\in[l,r]\) 中的點可以恰好構成一條鏈。\(N,M\le 2.5\times 10^5\)。
“一條鏈”的條件是很復雜的,考慮進行拆解:
- 沒有環;
- 每個點度數不超過 \(2\);
- 連通塊個數為 \(1\);
很顯然這三個限制疊加就和鏈的限制等價,那么考慮對於每個右端點 \(i\),左端點最前面可以達到什么位置,使得這個區間不存在環,定義為 \(f_i\)。同理對第二個限制定義 \(g_i\)。
假設通過某種手段求出了 \(f,g\),那么只要統計有幾個 \(j\in [\min(f_i,g_i),i]\) 滿足區間中的點只有一個連通塊即可。根據“點減邊”的套路,我們可以維護每個左端點與當前右端點的點減邊結果,計算有幾個是 \(1\) 就行了。考慮線段樹維護這個東西,每次右端點右移,左側的所有值 \(+1\),然后遍歷當前右端點的邊將邊的影響加上即可。怎么維護出 \(1\) 的個數呢?注意到一張不空的圖的連通塊不可能有 \(0\) 個或者更少,也就是說 \(1\) 是可能的最小值,而最小值的個數維護有手就行。這部分復雜度 \(O(N\log N)\)。
考慮求 \(f,g\) 怎么搞。首先一個重要的性質:如果 \([j,i]\) 不合法,那么 \(\forall k<j, [k,i]\) 都不合法。掏出一個雙指針,用一個 LCT 整出 \(f\),開個桶整出 \(g\) 就行了。這里復雜度 \(O(N\log N)\)。
最后思考一個問題:為什么預處理的 \(f,g\) 不能適用第三個限制?因為連通塊個數並不隨着左端點的左移而單調不增或不減。
F - Interval Graph
給定 \(N\) 個閉區間,第 \(i\) 個為 \([s_i, e_i]\),權值為 \(w_i\)。定義這 \(n\) 個區間組成的“區間圖”為,\(i, j\) 之間存在一條無向邊當且僅當 \([s_i, e_i]\cap[s_j,e_j]\ne \varnothing\)。現在要求移除其中若干個區間,使剩下區間構成的區間圖為森林。求保留區間權值和的最大值。\(N\le 2.5\times 10^5, 1\le s_i\le e_i\le 5\times 10^5,w_i\in [1, 10^{12}]\)。
首先考慮一些區間的區間圖是森林意味着區間之間有什么特征:森林說明沒有環,而一個區間圖不存在環的充要條件是:不存在一個位置 \(p\),使得存在三個互不相同的 \(i, j, k\) 滿足:\(p\in [s_i, e_i]\cap [s_j, e_j]\cap [s_k, e_k]\)。也就是說不能有一個位置上面覆蓋三個及以上個區間。
那么我們考慮一個網絡流模型:
- 一開始我們有 \(2\) 的流量:一個位置最多可以同時存在兩個區間。
- 對於每個區間 \([s_i, e_i]\),我們要走就會把他走完,那么連邊 \(s_i \to (e_i + 1)\),容量為 \(1\),表示在 \([s_i, e_i]\) 的位置這個區間我們可能會消耗一個流量。費用顯然為 \(w_i\)。
- 對於每個位置 \(p\),連邊 \(p\to (p+1)\),容量為 \(2\),表示兼容該位置選零到二個區間三種情況。
然后發現要保留盡量大的,於是將費用取相反數,就可以跑費用流了。
然后 SPFA 貌似死了,考慮使用 Dijkstra,但很顯然有負權,而傳統解決方案中的勢能需要 SPFA。
但是我們發現這個圖是個 DAG!所以 dp 一下就可以求出勢能了。
還有更簡單的方法:設第 \(i\) 個位置的勢能為 \(-10^{12}\times i\) 即可。但是這個我寫炸了
最后時間復雜度 \(O(N\log N)\)。
H - Alchemy
有一個可重集合,元素 \(i(\in[0, N ))\) 有 \(c_i\) 個。每次可以選擇一個子集 \(S\),在原集合中刪去這些元素,重新插入一個 \(\text{mex}(S)\)。求最終可以得到的最大的權值的元素。\(N\le 10^5\)。
考慮將操作倒過來:每次選定一個元素 \(k\),然后:
- \(k=0\):插入任何數量的任意 \(>0\) 的元素;
- \(k\ne 0\):插入任意數量(\(>0\))的 \(<k\) 的元素,\(>k\) 的隨意。
如果以上操作若干次之后得到了原集合的子集那么 \(k\) 是可行的。
實際上可以將上面操作簡化:
- \(\{k\}\to \{t| 0\le t< k\}(k>0)\)
- \(\{0\}\to \{k\}(k>0)\)
很顯然新加入的多了不會比少了更容易成為原集的子集,於是這樣簡化是正確的。
那么對於一個 \(k\) 從右往左掃一遍就能 \(O(N)\) 判斷是否可行。
然后發現這個東西單調,可以二分答案,復雜度 \(O(N\log N)\)。
J - Remote Control
在一個二維平面上,有一個在 \((0,0)\) 處的障礙。一開始給定一個操作序列,長度為 \(N\),序列中的每個操作為上下左右只一。現有 \(Q\) 次詢問,每次在 \((x,y)\) 放一個遙控車,並執行一次操作序列,遇到前面有障礙則忽略這一次操作。求最終位置。\(N,Q,|x|,|y|\le 3\times 10^5\)。
考慮每次移動車的復雜度是 \(O(NQ)\) 的,無法承受。那么不妨換個角度:車有很多個,但障礙只有一個對吧。那倒不如,直接一次性放完所有的車,然后用障礙去推,而不是用車去模擬。
具體的,先保存所有車的信息,然后對障礙執行操作序列,只不過都是反操作(左對右,上對下)。如果碰到了有車的位置,那么把這些車往對應方向再擠一個單位。
實現時可以用一個 std::map<std::pair<int, int>, std::set<int> >
保存每個位置對應的車的集合,推車時做一遍 std::set
的啟發式合並就能保證復雜度。
最后算答案時,將期望移動到的位置算上推車的影響即可。復雜度 \(O(N\log^2 N)\),當然也有 \(O(N\log N)\) 的方法。
K - Sewing Graph
有一塊布,上面有 \(N\) 個點 \(\{(x_i,y_i)\}\),你需要求一個序列 \(\{s_i\}\),設長度為 \(K\),對於 \(1\le i< K\),若 \(i\) 為奇數,在布的正面連接 \(s_i,s_{i+1}\),反之在反面連。若最終正反面的圖都聯通,則 \(\{s_i\}\) 合法。求構造一個最短的合法的 \(\{s_i\}\)。\(N\le 1000, 1\le x_i,y_i\le 10^9\)。
Solution 1
考慮 \(N\) 個點排成一條直線的情況:顯然依次連過去就是答案。
那么怎么對任意的點集可以“依次連過去”呢?
將所有點以 \(x\) 為第一關鍵字,按 \(y\) 為第二關鍵字排序,最后直接連肯定不相交。復雜度 \(O(N\log N)\)。
Solution 2
假如說兩對點 \((a,b)\) 和 \((c,d)\) 之間的線段有交怎么辦?換成 \((a,c)\) 和 \((b,d)\) 之后發現歐幾里得距離和一定更小。
於是暴力 Kruskal 求出歐幾里得最小生成樹,然后正反兩面都可以用這棵樹。最后對這個樹求個歐拉序就行了。復雜度 \(O(N^2\log N)\)。