因為一些奇怪的理由也來做集訓隊作業了。
基本上完成了任務,可能不會再更新了 /kk
[NEERC2013]Dictionary
Description
給一些單詞,要求建一棵節點最少的字典樹,使字典序中包含給定所有的單詞(單詞路徑在樹上出現過)。
Solution
若 \(a\) 是 \(b\) 的子串,那么 \(b\) 的限制強於 \(a\),\(a\) 可以刪去。
接下來我們就得到了兩兩不包含的單詞集。
考慮如果要把單詞 \(x\) 插入到當前字典序,可以找一個包含 \(x\) 最長前綴的位置,這樣可以添加盡量少的后綴湊滿整個單詞,並且這步並不影響后續單詞加入,因為字典樹能表示的串還是一樣,因此是獨立的。並且 \(x\) 最長前綴一定僅屬於一個字符串,否則即跨越了兩個字符串,即 \(x\) 包含那個字符串,這種情況已經去掉了。
因此對於兩個單詞 \(a, b\),可以設 \(w(a, b)\) 為把 \(b\) 接在 \(a\) 后面的最小花費,即 $len_b - $ \(b\) 的前綴與 \(a\) 能匹配的最長長度,這個東西可以 KMP 快速求,不過此題沒有必要。
把這個東西看成一個有向圖,這樣構成一個聯通的字典樹 \(\Leftrightarrow\) 該圖聯通。
枚舉根,求最小樹形圖即可。
這題還要記錄方案,考慮朱劉算法的定義,縮點的新邊選擇的意義是選擇這條邊,去掉原來的入邊。每一層每種新邊的選擇對應選1殺1,因此每次新邊可以新開一個編號,在最終狀態,考慮每個選擇編號對應上一層選擇不選哪兩條邊即可。把邊弄出來之后,需要構造字典樹,這里注意每個點可能代表若干串的某個下標,而邊的含義是某點要在某點的某處往伸,我是把邊預處理成最靠近根的點的位置。
時間復雜度 \(O(n^4)\)。
[CERC2015]Export Estimate
Description
給你一個無重邊、自環的無向圖,有 \(q\) 次詢問,每次詢問一個闕值 \(t\),問你把圖中 \(< t\) 的邊刪掉以后,從小到大編號考慮每個點,如果孤立點就刪掉,如果度數為 \(2\) 就把它變成一條邊(不能是自環) ,問最終狀態的點邊數,點邊詢問數都是 \(\le 3 \times 10^5\) 的。
Solution
發現兩種操作均不影響每個點的度數,第二個操作可以看作把一個點連向兩個點轉化成了一個邊。
而點邊的刪除的條件都是點的度數,因此考慮點的順序並不影響圖的結構,只會影響刪除/留下點的編號,但這個東西對我們沒用。
那么我們就可以用一個經典的思路,把邊從大到小排序,依此插入邊,考慮當前的圖對應的點邊結構是多少,然后加入那些處於這個狀態的答案。我們考慮何種情況的一個點會被刪除:
- 度數 \(= 0\) 的點。
- 度數 \(= 2\) 的點且最終狀態並不是一個自環。並不是一個自環的條件只能在純粹的環(即所處聯通塊只有一個簡單環,沒有其他冗余的東西)中取到,想象一下它會以此把編號最小的點換成一條邊,最終會留下一個編號最大的點的自環。而其他情況,不可能出現度數為 \(2\) 的點連的是自環的情況,如果不構成一個環,那么原來圖里沒自環,連接的兩個方向點必不可能縮成同一個點(大小為 \(2\) 的純粹環)。如果構成一個環,但環上有其他雜邊,那么不會縮成只有一個點,因為必然有一個點度數 \(> 2\)。
因此最終狀態點的數量 $= $ $n\ - $ 度數為 \(0\) 的點數 \(-\) 度數為 \(2\) 的點數 $+ $ 純粹的環數
然后再考慮何種情況邊的數量會改變:
- 度數 \(= 2\) 的點且最終狀態並不是一個自環 的點,會讓兩條邊數變成一條邊,道理同上。
因此最終狀態點的數量 $= $ $m\ - $ 度數為 \(2\) 的點數 $+ $ 純粹的環數。
其他東西維護都比較簡單,純粹的環數等價於該聯通塊每個點度數 \(= 2\)。用一個並查集維護聯通塊中度數為 \(2\) 的點數就好了。
時間復雜度可以做到線性(不排序,用桶,並查集兩個優化做到常數)。
[NEERC2016]Delight for a Cat
終於 TM 不看題解做出來一道(瞄了一眼標簽)
Description
你要構造一個長度為 \(n\) 的 \(01\) 序列,每個位置選 \(0/1\) 的權值給定。
要求在任意 \(k\) 連續段的 \(0\) 的數量大於 \(m_s\),\(1\) 的數量大於 \(m_e\),在這種狀態下最大的總權值。
還要輸出一種方案。
Solution
這題和 NOI2008 志願者招募 挺像的。
看起來很像 DP,但 DP 要記錄每個點選的啥才能保證滿足限制,因此不能用 DP。
發現要滿足的限制是一個帶變量的不等式,我們需要在限制內,選擇一種變量的方案,使答案最大化。
可以考慮設置一些變量,變不等式為等式,利用網絡流的流量守恆,使每個點等於一個等式,我們只需要將等式變換讓每個變量最多出現在一個等式的左側和另一個等式的右側(或者只出現一次):
- 若出現兩次,讓右側的等式點指向左側的等式點連一條邊。
- 若出現一次,若右側則讓這個等式點指向網絡流虛擬匯點連一條邊。
對於等式中的常量,左右側連和源匯點連邊,考慮到費用流首先保證最大流,因此一定會讓它充滿。
這條邊的流量就映射着這個變量的選擇,因此變量的形式應該是 \(0 \le x \le a\)。(\(x\) 為變量,\(a\) 為常量),這樣可以保證每個變量在所有等式中是同一個值。
因為睡覺和吃是補集關系,我們不妨只考慮在那些位置吃飯。設最初我們都選擇睡覺,然后考慮每個點的權值為 \(e_i - s_i\),即從睡覺到吃變換的權值。
設 \(x_i \in \{ 1, 0\}\) 為第 \(i\) 天是否選擇吃飯,那么需要滿足對於任意 \(K \le i \le n\), \(m_e \le \displaystyle \sum_{j=i-k+1}^i x_j \le k - m_s\)。
化不等式為等式,設 \(t_i\) 為這個等式的一個闕值,滿足 \(0 \le t_i \le k - m_s - m_e\),這樣上面那個式子就等價變成了:
但是呢現在每個變量出現在了多個等式中,考慮把等式進行等價變換,保留 \(i = k\) 的等式,\(i > k\) 的等式做一個差分,等價變成 \(i\) 減去 \(i - 1\) 的等式,如此,我們得到了 \(n - k + 1\) 個等式:
- \(i = k\):\(\displaystyle \sum_{j=i-k+1}^i x_j + t_i = k - m_s\)
- \(i > k\):\(t_i + x_i = t_{i-1} + x_{i-k}\)
然后我們發現變量就最多出現在兩個式子中了,因此跑最大費用最大流就好了。
還要輸出方案,根據實際意義在網絡流里看對應邊有沒有流滿就好了。
網絡點邊數都是 \(n\) 級別的。由於網絡流不容易卡滿,因此可以跑過。
[NEERC2016]Binary Code
終於不看題解做出來第二道(瞄了一眼標簽),一開始還口胡錯了。。
Description
給你 \(n\) 個 01 串,每個串至多有一個 \(\text{?}\) 你可以選擇讓他變成 \(0\) 或 \(1\),求是否存在一組方案使得兩兩串都不互為前綴, \(n, \sum |S| \le 5 \times 10^5\)。
如果存在還要輸出一種方案。
Solution
每個串只有兩種狀態,即一個 2-SAT 問題。
暴力的考慮,任意兩個不符合的狀態都要連兩條有向邊,這樣是 \(O(n^2)\) 的。
考慮前綴的關系是一個線性包含關系,如果我們把這些狀態串放到 01Trie 上,那么每一條鏈上的點最多選一個,即選一個點的影響是它的子樹與祖先上的狀態都不能選。
我們可以再創兩個虛點,一個叫下傳點,一個叫上傳點,兩個點都指向自己的另外一個狀態。下傳點向所有兒子連邊,上傳點向父親連邊。(這里的兒子父親都是指在 Trie 上同一條鏈上,當前點的狀態串對應的前驅后繼狀態串編號,並非 Trie 的點,如果是本質相同的串,先后順序沒有影響,都是選一個會把其他給否掉)。
這樣選擇一個編號我們就能以他的度數量級的有向邊表示他的限制關系里。
度數和虛點都是和 \(n\) 同階的。
因此復雜度 \(O(n + \sum |S|)\)。
[CERC2014]The Imp
這種每次要從里面選一個物品買,方案是一個排列的,\(n\) 非常大,通常是證明最優決策在一條鏈上,然后排個序背包。
假設證明的了最優的決策在一個線性順序上,即按 \(1 \sim n\) 的順序考慮是否買物品,那么可以列出如下 DP:
- 設 \(f_{i, j}\) 考慮完 \(i \sim n\) 的物品,用了 \(j\) 次咒語的最大收益:
- \(f_{i, j} = \max(f_{i+1, j}, \min(f_{i+1,j+1}-c_i, v_i - c_i))\)。外面的最大化表示是你的自由選擇,里面的最小化是小鬼的自由選擇。
考慮物品的排列假設是 \(p\),設 \(x = p_i, y = p_{i+1}\),我們考慮何時交換不優。
考慮把這兩個物品的貢獻打包起來,轉移應該是形如 \(f_{i,j} = \max(f_{i+2,j}, f_{i+2,j+1} + k, f_{i+2,j+2} - c_x - c_y)\),其中 \(k\) 是個常量,打包后如果是這期間用了零或二次咒語,順序沒有影響,因此只有用了一次又影響。
交換前,這個 \(k = \min(v_x - c_x, v_y - c_y - c_x)\)
交換后,\(k = \min(v_y - c_y, v_x - c_x - c_y)\)
即處理這個東西:
發現左邊的第二項恆小於右邊的第一項,右邊第二項恆小於左邊第一項。等式成立等價於 \(v_x - c_x - c_y\) 成為四者最小值,因此這兩項不可能作為這個式子的最小值,上述式子就等價於:
因此按 \(v\) 排序做背包即可。
復雜度 \(O(\sum nk)\)
[ICPC2019 WF]First of Her Name
md 自己思維僵化想了好久。。結果這么模板的題還是需要瞄題解。
把那個名字看成一個字典樹,把詢問反轉,就變成了這個詢問字符串能作為字典樹上有多少點的后綴。
考慮 AC 自動機的 fail 樹,這個東西就是把一個串的后綴都放到了他的祖先鏈上。因此只需把 fail 樹建出來,\(n\) 個位置的權值是 \(1\),求一遍子樹權值和即可。
[CERC2017]Kitchen Knobs
考慮 \(7\) 是個質數,因此要么一個輪盤數字全一樣就是怎么轉就行,即沒有限制,要么就是有唯一的旋轉位置。把怎么轉都行的剔除掉。
考慮把這個環的東西弄成模意義的數值問題,設位置狀態是 \(i\),意義即順時針轉 \(i\) 次的狀態,在 \(\bmod 7\) 意義下。那么問題就變成了一個序列,最初全為 \(0\),有一個目標狀態 \(a\) 數組,每次可以連續選一段 \(+k\),問最少操作多少次變成目標狀態。
考慮是操作是互逆的,可以將初始和目標反轉,又考慮這個連續的一段操作,可以用差分變成兩個點的操作,設 \(b\) 為 \(a\) 的差分數組,那么問題轉化為:
- 一個操作可以選擇一個 \(l < r\) 和 \(k\), 使得 \(b[l]\) 加 \(k\),\(b[r]\) 減 \(k\)。
問變成全 \(0\) 最少操作次數。
這個東西還是不太好做,考慮轉化。發現一個關鍵性質就是每次操作不改變 \(b\) 的和。考慮一組最優解,把每次選擇的 \(l, r\) 之間連邊,那么對於每一個大小為 \(cnt\) 的連通圖而言,需要滿足他們的 \(b\) 的和模意義是 \(0\),並且至少操作 \(cnt - 1\) 次,如果我們能構造出一種 \(cnt - 1\) 次數的答案。因此就可以說明一定存在一組最優解是分成若干組,並且使用的是我們的這種構造方式,那么我們只需要求這種方式的最優值就好了。那么問題就等價於:
- 把序列分成若干組,每組的 \(b\) 和為 \(0\)。設有 \(k\) 組,每組大小是 \(cnt_i\),答案即 \(\sum_{i=1}^k (cnt_i - 1) = n - k\)。
構造的證明:
考慮把這些點從左到右排序,依次是 \(p_1, p_2, p_3,..., p_{cnt}\)。
考慮第 \(i\) 次操作,前 \(i - 1\) 個都變成 \(0\) 了,現在讓 \(b_{p_i}\) 變成 \(0\)。
讓 \(l = p_i, r = p_{i+1}, k = 7 - b_{p_i}\) 就好了。
因為和是 \(0\),因此前 \(cnt - 1\) 變成 \(0\) 了,最后一個也是 \(0\)。
因此我們需要最大化分的組數。
直接求是一個非常大的背包不太可做。
我們先考慮把 \(a + b = 7\) 這樣大小可以為 \(2\) 的組給安排了。
那么最終 \(1, 6\) 、\(2, 5\),\(3, 4\),三種組,分配后剩下每組最多只有一種,因此剩下的數字種類最多 \(3\) 個。
用這三個(設為 \(x, y, z\))用的數量做一個三位背包就好了,考慮物品 \(i\times x +j\times y+ k \times z\),必須滿足 \(i, j, k < 7, \gcd(i,j,k) = 1\)(還有單獨七個特判),否則可以繼續分成兩半,更優,可以打表發現物品個數最多是 \(41\) 個。
設剩下的數字種類每個數分別 \(A_1, A_2, A_3\)。
那么時間復雜度是 \(O(\prod A \times 41)\)。
用均值不等式發現這個東西最大是 \(2e8\) 左右,可以苟過。
[NWRRC2016] Java2016
對於 \(0\) 的部分樣例給我們了。
剩下的情況我們只需要找到一種方式生成一個非常穩定的數 \(a\)。這樣先除,再倍增,進制拼湊,能在很少的字符數內生成任意一個數。
生成的穩定數可以用 \(\max\) 來湊,當 \(2^{17}\) 個 \(\max\) 拼在不是 \(255\) 的概率是 \((\frac{255}{256})^{2^{17}}\) 這個東西可以估計為無窮小,對答案沒有影響。
[NEERC2017]Laminar Family
第一道獨立口胡出來的(沒瞄標簽。。雖然是個簡單題。感覺和某一次做的 Div 3 最后一題很類似
考慮集合按所含點數大小從大到小排序,每次執行:
- 看這條路徑上是否有不同顏色的點,若有則 \(No\)
- 否則把這條路徑染成一種新的顏色
執行完畢就是 \(Yes\)。
正確性比較簡單,考慮假設是 \(No\),找到相交且長度相鄰的兩條路徑,在染一個的時候會發現它本身被比他更長的覆蓋了,但沒有被完全覆蓋,因此必然相交。如果是 \(Yes\),絕對不會存在這種情況。
這樣用樹剖 + 線段樹是 \(O(m \log ^2 n)\)
也可以考慮把上述操作逆序,即每次只需合並,查看當前路徑所在聯通塊是否不包括其他點,考慮合並的時候每次都把指針移到當前聯通塊的最上端,這樣每合並一次至少少一個聯通塊,故最多合並 \(n - 1\) 次,這樣是 \(O(m \log m)\) 的(排序。
[NWRRC2017] Consonant Fencity
簡 單 題。
把問題轉化成圖論上的問題。
答案顯然和序列順序無關,考慮一個相鄰的輔音字母點對 \(a, b\),在他們中間加一條邊。
考慮把這個圖每個點染成兩種顏色之一,那么他們的那個權值就是不同顏色之間點的邊權和。
那么我們反着考慮一個邊的集合怎么樣可以黑白染色,顯然是一個二分圖。
那么問題就變成了求最大權生成二分圖。
由於點數只有 \(26 - 7 = 19\) 個,可以暴力枚舉一組點集暴力更新答案 \(O(n^2 2^n)\),也可以遞推做到 \((n2^n )\)。
[NWRRC2014] Kebab House
即選出一個 01 串(\(0\) 代表做夢),滿足:
- 相鄰的 \(0\) 之間的 \(1\) 的數量 \(> t\)
- 每段的 \(1\) 總和滿足限制
不妨按段 \(DP\)。
維護一個 \(f_i\) 表示之前最后一個 \(0\) 的位置距離當前第一個位置的距離,前面滿足條件的方案數。把 $ > t + 1$ 的方案記錄在 \(f_{t +1}\) 上,因為轉移的限制相同。
轉移時,只需考慮新段最后一個 \(0\) 的位置,再枚舉前面的 \(f\),之后中間的方案數對應的是在連續的一段選 \(0\),並且 \(0\) 的個數 \(\le q-x\) 的一個方案數,乘起來。
考慮預處理出連續的一段選 \(0\) 的方案數,可以再設一個 DP,\(g_{i,j}\) 表示長度為 \(i\),滿足限制的情況下選了 \(j\) 個 \(0\) 的方案數,最后一個位置的 \(0\) 為 \(i\)。這個可以直接 \(O(q^3)\) 枚舉上一個 \(0\),DP 出來。
然后整個 DP 大概可以用 \(O(ntq)\) 算出來。
可以通過此題。
[CERC2017]Cumulative Code
亂 搞 帶 師
我這個破東西的理論復雜度是 \(O(2^{14} \times k^2+ q(2^{22} + k))\) 的,不知道咋過去的。。
考慮暴力,就是看刪除的順序,如果是最右邊一條鏈,是左根右,否則是左右根,因此迭代遞歸下去就行了,一次復雜度上界大概是元素個數 \(\times k\)。
然后考慮優化,一般這樣的東西可以根號分治,考慮 \(d > B\) 的東西,不會訪問超過 \(\frac{2^k}{B}\) 個點。
然后較小的 \(d\) 可以記憶化,因為我比較菜,只觀察出了同一層,被整個區間包含,\(d, a\) 都相同的點可以記憶化,不過需要記憶一個 \(k\) 的數組,第 \(k\) 維代表深度為 \(k\) 的點有多少計入的答案。這樣的化,相同的這些點就可以通過編號的偏移量來實現 \(O(k)\) 獲得答案(在記憶化一遍以后),然后考慮不被包含的一層至多有左右兩個,這個東西需要開 \((Bk)^2\) 大小的數組,非常 8 行,然后取 \(B = 2^7\) 的時候才能開下。。
事實上正解貌似就是純粹的根號分治,但是我不會記憶化www
后來發現所以這樣記憶化沒必要,因為最多就 \(300\) 個不同的 \(d\)。
所以數組實際上開 \(Bk^2\) 就行了。單次時間復雜度還是 \(O(k + k^2B + \frac{2^k}{B})\) 因此取 \(B = k \times 2^{k/2}\) 最優。。
[CERC2014]Pork barrel
思維難度比較簡單,畢竟我都想出來了。。
考慮從大到小維護一個 \(\ge v\) 的邊的最小生成樹的所有邊,從大到小考慮插入邊,如果成了一個環就要扣掉原來最大的邊。這個東西點邊轉化后用 LCT 維護。然后插入刪除邊對應在線段樹對應位置加減權值,查詢時只需在對應時刻,查詢區間和即可。
但是要強制在線,上主席樹。
每條邊最多會被加入刪除一次,因此復雜度 \(O(T(n + (m + q) \log m))\)。
第一次寫 LCT 練習題發現之前自己的板子是假的。。
[NEERC2014]Improvements
想了很久很久,最后好家伙看錯題了。
相交指的是邊相交。。
問題可以轉化為每次在一個位置 \(p\),選擇一個方向走若干距離,然后在新的位置立一個標記(即第 \(i\) 次的位置),途中不能跨越之前的標記(可以走到之前的)。
然后這樣的一個東西一定就是在當前范圍內一直走,要么就是回頭,然后把范圍分成了更小的。
把一個合法狀態每個位置所在點編號寫出來,發現是一個單峰序列,顯然也比較好證明,每次標記只會在最內層制造一個比之前都大的編號,當然是單峰的了。
因此問題變為了改變至少若干編號的位置,讓這個東西變成單峰的。
每次改變最多讓最大單峰長度 \(+1\),而且可以做到。
因此能不改變的最大長度就是原序列的最大單峰子序列,正反跑 LIS 拼起來就行了。
[ICPC2014 WF]Metal Processing Plant
判定性問題比較好做,可以 \(O(n^2)\) 做 2-SAT。但這樣復雜度是 \(O(n^6)\)。可以枚舉一條最大邊,然后二分次大邊(固定了最大邊以后符合條件次大邊的值顯然是一個后綴),這樣是 \(O(n^4 \log n )\),雙指針可以把 \(\log\) 去掉(然后再亂搞一下可以卡常過此題)
然后的奇偶環優化非常的神仙,考慮從大到小枚舉最大邊,然后把之前所有邊構成的聯通塊用並查集維護:
- 如果構成了奇環,考慮染色后,必要條件就是在現在的圖上邊沒有相鄰的顏色,但此時已經有奇環了,所以必然不可能存在最大邊 $< $ 當前邊的情況,可以做完一次直接 \(\text{break}\)
- 如果構成了偶環,那么你考慮要用到這個作為一個集合之內最大值,用到它就說明兩端要同色,但這兩端又隔奇數個點,所以沒法做到這點,理由類似奇環,可以 \(\text{continue}\)
- 否則二分次大邊做一遍
這樣只會做 \(n \log n\) 次 2-SAT(別忘最大邊可以是 \(0\)),總復雜度是 \(O(n ^3 \log n)\) 可以通過。
[NWRRC2014]Fragmentation
考慮降序的必須斷開,離散化后不連續的必須斷開,序列就變成大概是若干遞增串(稱之為塊,給這樣的每個塊編個號qwq)的形式,然后可以連起來的段要滿足的性質(充要):
- 如果三個以上不同數值連起來,被包含在中間的數必須恰好所有同數值都在這個段里,否則就拼不進來了。
- 離散化后,連接 \((x, x+1)\) 這樣的東西最多連一次。
如果假設最開始全斷的化,要最大化連的次數。
這個東西似乎很難做,每段並不是獨立的,看樣子需要按數值順序做安排。
想到(我就想不到嗚嗚嗚)如果 \(\le x\) 要如何連都安排好了,那么后續再連,前面對后面決策的可行性有影響的只有 \((x - 1, x)\) 有沒有連,連的哪個塊:
- 如果 \((x, x+1)\) 不准備連那么不影響
- 若 \((x, x +1)\) 連的塊不是 \((x - 1,x )\) 的塊,那么顯然咋搞都行
- 如果是相同的塊,那么就要檢查是否 \(x\) 全部出現在這個塊作為可行的要求。
因此我們可以設一個 DP:
-
\(f_{i, j}\) 表示連接到數值 \(i\),\((i - 1, i)\) 連的塊編號是 \(j\)(沒連搞成 \(0\)),最大化連的次數。
-
轉移就枚舉 \((i - 2, i - 1)\) 連的塊是啥
-
\(f_{i,0}=\max(f_{i-1,j})\)
-
\(f_{i, j} = \max_{k\not= j}(f_{i - 1, k} + 1)\)
-
同一塊內的轉移要滿足 \(i - 1\) 全部出現在這個塊里,式子也是同上。
-
別看是二維 DP,但實際的狀態數應該是 \(O(n)\) 的,因為每一個狀態都一一對應着一個塊內連續對。
然后考慮轉移,暴力轉移是 \(O(n^2)\) 的。優化也挺顯然,前后綴 \(\max\) 再加自身轉移特判,每次轉移做到 \(O(1)\)。(具體實現來回掃一遍雙指針)
總復雜度 \(O(n)\)。
[ICPC2014 WF]Pachinko
設 \(f_{i, j}\) 為球到這個位置的概率,不難列出非障礙物數量級的有后效性的方程組,裸高斯消元解是 \(O((nm)^3)\) 的,但是這個方程組每組只有 \(5\) 個數系數非 \(0\),並且在長度為 \(2m\) 的連續的一段 \([i-m, i+m]\) 內。
考慮高斯消元的過程,每次把前面的都消掉,然后把后面的影響加上。
那么每次操作過后,任意一個方程組的系數仍然在 \([i - m, i +m]\) 的范圍內。
這樣就可以開一個 \(O(nm^2)\) 的數組記錄系數,每次操作的影響也變少成了 \(m\) 組。
因此總復雜度 \(O(nm^3)\)。
(此題貌似無法約旦,系數會偏移)
至今仍不清楚為什么此題有的地方解不出來。
[CERC2017]Buffalo Barricades
這題的關鍵想法是,現把終狀態每個點唯一占據的數量算出來 ①,再加上每個牛被算多次的貢獻 ②。
這樣講占據的總和變成 \(n\) 級別的。
步驟 1
你考慮一個人向左向下圈地的過程,比如向下,僅當他碰到了比他更靠右更靠下,且時間比他早的這樣個東西,他就會停止。
那么你可以做一個掃描線,從右往左,維護每個人的縱坐標在一個 \(set\) 里。
這個 \(set\) 的意義表示,一個人管轄的縱坐標范圍是其在 \(set\) 到他的前驅。
考慮加入一個人的影響,如果這個人前驅的時間大於他,那么這個前驅就會被這個人的柵欄擋住,在后續 \(\le x\) 的過程都不會有他占領的范圍了,可以刪掉,反之則這個人被前驅擋住,保留。
這樣就維護了一個縱坐標管轄范圍,對於每個牛,就在 \(set\) 上找到對應管轄的人 ,,lower_bound 一下就行。
注意到最多刪 \(m\) 次,因此復雜度 \(O((n + m) \log m)\)。
步驟 2
考慮 \(a, b\) 兩個人,何時 \(b\) 占據的牛會對 \(a\) 占據的牛產生貢獻,必須滿足:
- \(a\) 的范圍完全包含 \(b\)
- \(a\) 的時間比 \(b\) 早。
- \(a, b\) 中間的點時間都比 \(a\) 晚。
我們把 \(a, b\) 之間沒有點,即 \(b\) 是最小包含 \(a\) 的點這樣的東西,連一條 \((b, a)\) 的邊,形成了一個樹形關系。
這個邊也很好找,就在步驟 \(1\) 中,事實上在那個時刻把這個人當做一個牛,找包含他的店就好了。
那么 \(b\) 對 \(a\) 有貢獻當且僅當 \(b\) 是 \(a\) 的兒子,並且 \(a, b\) 之間的點不包含 \(a\) 出現時刻都比 \(a\) 早。
那么算法便很好設計了,考慮時間倒流,處理到 \(a\) 時,把時間 \(>\) 的點全跟父親連起來,\(a\) 的答案就是當前所在聯通塊的權值。
只有合並聯通塊的操作可以並查集。
[NEERC2015]Cactus Jubilee
-
如果斷掉的是樹邊,兩個點分別在斷成的兩個聯通塊上,否則不能聯通。這部分在一遍 dfs 中很好算出。
-
如果斷開的是環邊,加上還是仙人掌,即選擇的兩點之間在斷開的新圖上都是樹邊。那考慮只保留樹邊。答案就是每個聯通塊任選兩個的和。那你考慮選擇的環邊產生的新圖,只是把這個環上延伸出去的樹邊加上這個環上的點,剩下的部分沒有變。而且一個環上斷邊上形成的各塊大小一致,所以可以一起算。因此每次枚舉一個環,然后考慮斷一條邊合並了若干原圖上的橋,計算一下貢獻即可。復雜度是線性的。
[ICPC2019 WF]Karel the Robot
是不是就模擬就好了。
考慮記憶化一下,記錄 \(f_{x, y, d, c}\) 在 \((x, y)\) 方向 \(d\) 執行命令 \(c\) 最終會走到哪里,狀態數是 \(4rcd\) 每次轉移大概是 \(100\) 次。
永不停止並不可怕,只要在記憶化搜索的過程中走到相同的狀態即可,怎么記錄這個東西呢?考慮用逛公園的思想,把當前搜索的 dfs 樹用 vis 賦值一下,如果又走到的就 \(-1\)。
后來發現因為有 u 的存在,這個復雜度大概是不對的,正解還要記憶化每次執行命令到哪里了。
這樣才能嚴格的控制在 \(4rc \times 36\times 100 \le 2 \times 10^7\) 的計算量級。
反正他過了,而且沒被卡,就不管了。
[NEERC2014]Epic Win!
我發現集訓隊的題都好好玩啊。
現考慮如果我們知道了對方當前狀態,我們就可以建立一個 \(n\) 的節點,稱之為一個系統,第 \(i\) 個節點設定為打敗對方 \(i\) 號節點的狀態,並且隨之轉移到下一個點,對着讓他失敗着卡就行了(
但是我們不知道,咋辦呢?
一種想法是通過有限次弄到一個確定的位置,但顯然他的構造(也許有循環性等等)不能讓我們的統一化成立。
考慮利用模擬退火類似的方式,我們可以做 \(500\) 個系統,每次隨機一個猜當前狀態的位置,一旦失敗,就轉移到下一個系統,這樣失敗的概率在 \(n = 100\) 時是 \((\frac{99}{100})^{500} \approx 0.6 \%\),這個東西感覺非常的小,基本不可能發生。
復雜度大概就是 \(O(500n)\)
[NEERC2014]Hidden Maze
即要算出隨機樹,任意奇數路徑中位數期望。
拆成和除以總數,總數可以用簡單的樹形 DP 在 \(O(n)\) 內得到(考慮在 LCA 那里算一下貢獻)
考慮所有情況中位數的和怎么做。
那個隨機樹的性質告訴我們樹的深度大概非常小。
考慮算每條邊作為中位數的貢獻,考慮當你確定的中位數(為了避免數值相同貢獻的疊加,同值可以人為規定一個順序),把 $< $ 這條邊視為 \(-1\),\(\ge\) 大的邊視為 \(1\),即找到經過這條邊且權值和為 \(1\) 的數量就是這條邊的貢獻。
為了讓復雜度和深度有關,我們考慮設一個 DP,設 \(f_{i, j}\) 為 \(i\) 節點向下的一條路徑,且在目前狀態邊權和為 \(j\) 的數量,由於深度很小,因此 DP 數組不會很大。
考慮把邊權排序,然后從小到大考慮每條邊貢獻,目前需要做的事是:
- 統計經過這條邊邊權為 \(0\) 的數量。
- 將這條邊權從 \(-1\) 變成 \(1\) ,改變對應的 \(f\) 數組。
我們發現只有祖先的 \(f\) 值會改變,因此做到了一次深度級別。具體實現時,我們可以先把祖先到這條鏈的答案消除,然后枚舉第一步貢獻的 LCA 手動計算一遍,再設一個類似的 DP 數組算出僅考慮經過這條邊的一些 DP 值,然后把兩個合並貢獻進答案里,之后再更新祖先的 DP 值。
這樣復雜度大概每次是深度的平方,但是可以通過(
設深度為 \(d\) 大概上界是 \(O(nd^2)\)。
不知道為啥只能過評測鴨,不能過 CF(我太慢了。
[NEERC2016] Indiana Jones and the Uniform Cave
一道神仙題,首先只給我們三個狀態,而且提到強連通,因此可以將石頭的狀態分別代表節點的三個狀態:未訪問 Center / 在當前棧(同 tarjan 算法)Right / 已經訪問完畢 Left
然后考慮進行類似 Tarjan 的算法,當前到根的 DFS 樹代表正在處理訪問完所有邊的點,讓當前石頭所在的通路位於當前考慮到的最后一次訪問的邊。對於每一個點總體執行 \(m\) 次 1 right 1
,然后我們需要考慮的是:走到一個非新點如何回來,以及這個點處理完畢如何回到父親。
- 考慮如果走了一條邊 \((u, v)\),\(v\) 的狀態是 Right,即在棧上,即是 \(u\) 的祖先,那么沿着當前的路
0 left 0
直到走到第一個為 Left 點,這期間經過的點數設為 \(y\),那么即 \(v \Rightarrow u\) 在 dfs 樹上有 \(y\) 個點,那么我們只要走 \(y - 1\) 次,每次都0 right 0
,就回到了 \(u\),並且還原了狀態。 - 如果走到 \((u, v)\),\(v\) 的狀態是 Left,即已經處理完畢了,我們要盡量變成 1 的步驟,即盡量走到棧里,考慮這是一個強連通圖,那么必然有 \(low_u < dfn_u\),我們每次只要走到自己那個更新自己 \(low\) 的走就一定能走到根節點。之后的問題同 1。即走兩次 1、2 的環路。
- 如果 \(u\) 處理完畢,要回到 \(fa_u\),差不多同理 2,現走到祖先鏈然后同 1 跑到 2 即可。
在 2、3 補中我們需要這樣的一個操作,在 Left 節點讓他可以走到讓他 low 小的點。那么我們只要 DFS 結束時讓石頭指向這個位置即可。考慮根據 Tarjan 算法類似進行處理即可,注意一個大坑就是無論此時在不在棧中都可以用 dfn 取更新,這是因為我們並沒有真正的進行縮點,因此此處的部分有可能可以幫我們連上去(。
覆蓋一條邊的代價不會超過 \(2n\) 次,總次數 \(2n^2m = 16000\) 可以通過。
[CERC2014]Mountainous landscape
如果給我們一個區間 \([l, r]\),我們要看這里面的點有沒有可能作為答案,即 \([l, r+1]\) 區間有沒有折線有部分在該線上方,這個很容易做,可以在預處理這一段的上凸包(越靠上越好,那么被完全包含在內測的凸包的確是沒有用的)后 \(O(\log n)\) 得出,即二分出斜率大於當前線斜率的第一個折線右端查是否在這個線上方即可(還有一些細節,如沒有大於的就是第一個點)。
因此我們可以建一棵線段樹,每個節點預處理一個上凸包。
對於每個點,在線段樹上二分即可。時間復雜度 \(O(n \log^2 n)\),空間復雜度 \(O(n \log n)\)。
[ICPC2014 WF]Messenger
無解判斷顯然是如果從 A 起點出發直接走直線最快都無法截斷(在 B 到終點前到達)。
直接做不好做(無法刻畫距離時間間隔對等的關系),考慮二分答案 \(x\),判斷郵遞時間是否存在 \(\le x\)。
問題等價於,A 從起點出發,B 從原線路走到 \(x\) 時刻出發(時刻對齊),是否有一個時刻使兩者的距離 \(\le x\)。證明: 如果滿足,顯然存在(不斷縮小間隔讓距離和間隔相同即可)。反過來如果存在一個答案 \(ans \le x\),你可能通過拉長時間間隔讓他們變成 \(x\),由於然后他們距離的變化不會超過時間間隔(你考慮兩點 \(a, b\) 之間的距離,當 \(a\) 移動 \(d\) 長度,距離變動會小於等於 \(d\)) ,所以可以得到一個時間間隔是 \(x\) 然后距離 \(\le x\) 的答案。
我們將時間段按照 \(n + m\) 個兩人走的轉折點分成若干段,每段 A、B 都是在一條直線上做勻速直線運動。
運 動 是 相 對 的。
可以轉化為 A 靜止,B 勻速直線運動。
即求 A 在 B 運動線段軌跡距離最小值,即點到線段最小值,就能做了。
時間復雜度 \(O(n \log n)\)。
[ICPC2019 WF]Directing Rainfall
口胡假了,還可以滾到一半挖洞,我自閉了。
考慮動態維護這樣一個 \(f\) 數組,\(f_i\) 表示當前從 \(i\) 位置最后到達葡萄園最少打洞數量。初始即 \([l, r]\) 是 \(0\),剩下是正無窮。
從下到上考慮加入一條線段的影響,設這個線段橫跨的區間是 \([a, b]\)
- 如果左邊高,那么對於 \(a < i < b\),會使 \(f_i = \min( (\min_{i \le j < b} f_j) + 1, f_b)\)
- 如果右邊高,那么對於 \(a < i < b\),會使 \(f_i = \min((\min_{a < j \le i} f_j)+1,f_a)\)
相當於先在 \((a, b)\) 區間加,然后在 \((a, b]\) 區間做一個后綴 \(\min\), 或在 \([a, b)\) 做一個前綴 \(\min\)。可以在線段樹上維護這個東西,記錄每個節點是否單調,以前綴 \(\min\) 為例,從左到右遍歷每個 \(\log\) 個極大被包含的區間,記錄一個闕值 \(v\) 代表左側已訪問過的最小值,如果這個值大於當前節點最小值就區間覆蓋,如果大於最大值並且這段單調遞減,就直接闕值為該節點的最小值然后退出,否則遞歸左右子樹。
這樣每訪問一個節點可以讓他變成單調的,區間加只會讓 \(\log\) 個節點不單調,大概是 \(O(n \log n)\)(俺也不會分析 /kk)
那如何從下往上排序呢?
我們要達到類似這樣的排序效果,要滿足任意一條豎線從下往上都是遞增的。
橫着做一遍掃描線,我們弄一個 set 動態維護掃描線上的編號序列,只要他們都在這條線上有交,那么必然有順序。我們插入一條線看起點這個位置的前驅后繼連一條邊就行了。證明:所有邊都是合法的,考慮相鄰的兩條線段一定存在一條有向邊,因此是 ok 的。