2024.10 做題筆記
10.2
隨機化和搜尋題還是太神秘
P9257 [PA 2022] Mędrcy
將每條咒語代表的不知道它的兩個人連邊,如果存在一個人不知道任何咒語,即圖是菊花圖,則第一天他就會離開
否則第二天相當於每個人知道了每個人都至少知道一條咒語,那麼如果一個人發現自己知道的咒語中有人一條都不知道,就說明那個人知道自己不知道的咒語,則他就會離開,去掉這個人相鄰的邊後,圖是菊花圖
歸納地考慮,第 \(k\) 天相當於每個人都至少知道 \(k-1\) 條咒語,將問題轉化為求圖中的最小點覆蓋,如果大小 \(\le x\) 則輸出所有可能在最小點覆蓋中的點
求任意圖的最小點覆蓋顯然是不可做問題,只能搜尋
環和鏈都能直接計算,如果想最佳化搜尋的複雜度則每次找度數最大的點 \(x\),此時 \(deg_x\ge 3\),列舉 \(x\) 是否在最小點覆蓋中,如果在則去掉 \(x\) 及其相鄰的邊,否則相鄰邊的另一端點必須在最小點覆蓋中
這部分的複雜度寫出遞推式,最壞情況下不選 \(x\) 能確定 \(3\) 個點,\(T(k)=T(k-1)+T(k-3)+O(n)\),由於最多隻會選 \(30\) 個點,複雜度暴力算出來大概是 \(O(1.4656^k)\)
複雜度咋算的?把後面看成 \(O(1)\),解特徵方程 \(x^3=x^2+1\),得到兩個根 \(A,B\),遞推式大概是 \(aA^k+bB^k\),取較大根是 \(1.4656\)
P4581 [BJOI2014] 想法
把組成新題目的兩個題目向新題目連邊,得到了 DAG,要粗略求每個點前驅中 \(1\sim m\) 的點數
點數太多了無法使用 bitset
,考慮近似演算法
需要知道一個前置結論,如果在值域 \([0,v]\) 上等機率均勻隨機取 \(n\) 個數,則最小值的期望為 \(\dfrac{v}{n+1}\)
反過來,如果知道某些這樣得到的數的最小值,能求出數的期望個數
那麼將 \(1\sim m\) 中的每個點賦予這樣的隨機權值,對剩下點拓撲排序求出前驅中的最小權值,求出期望個數,多做幾次取平均值,實測做了 \(200\) 次可以
P7450 [THUSCH2017] 巧克力
中位數最小則二分答案 \(x\),\(\le x\) 的看作 \(-1\),\(>x\) 的看成 \(1\),要求選出的連通塊權值非負
\(k\) 很小,隨機將每種染上 \([1,k]\) 中任意一種顏色,要求選的顏色不同,正確率為 \(O(\dfrac{k!}{k^k})\)
然後就可以跑最小斯坦納樹,求最小權值即可,實測每次二分隨機 \(T=150\) 次可以,複雜度 \(O(Tnm3^k\log V)\)
#3400. 「2020-2021 集訓隊作業」Storm
顯然選出的街道集合不會成環,一定組成二分圖,且最多涉及 \(k+1\) 個點
如果原圖是二分圖,可以建立費用流模型,\(S'\) 連左部點,右部點連 \(T\),每個點由於只能貢獻一次,連流量為 \(1\),費用為點權和流量為 \(\infty\),費用為 \(0\) 的邊,左部點和右部點的連邊,流量為 \(1\),費用為負邊權,\(S\) 連 \(S'\),流量為 \(K\),費用為 \(0\),跑最大費用可行流
如果原圖不是二分圖,把所有點隨機染成黑白兩種顏色,忽略同色點之間的邊,這樣染色正確的機率為 \(\dfrac 2{2^{k+1}}=\dfrac 1{2^k}\),隨機跑 \(T2^k\) 次費用流即可,\(T=10\) 可以透過
費用流的複雜度 精細分析 可以得到為 \(O(k^2(n+m))\),大概是因為每次 spfa 每個點入隊次數為 \(O(k)\) 次
「NOI2022模擬賽czx1」鏈式進化
怎麼 acc 上模擬賽還有這個題……居然題解還給了確定性做法
\(O(n^2\log n)\) 就是對每行單獨二分答案做,二分答案後雙指標統計區間數即可
最佳化掉這隻 $\log $ 的方法第一種就是隨機打亂所有行,因為隨機排列的字首最大值個數期望為 \(O(\log n)\),每次只需要先 \(O(n)\) 判斷這行比前一行大時的答案是否可行,如果可行再二分,期望複雜度 \(O(n^2+n\log^2n)\)
但這也給我們啟發,我們不需要隨機化的性質,由於 \(\operatorname{mex}\) 值域為 \(O(n)\),因此我們直接把行按 \(w\) 從大到小排序,則後面的行取到的答案必須增加,維護當前取到的答案是多少即可,如果符合就不斷將答案 \(+1\) 並判斷,複雜度是確定的 \(O(n^2)\)
Warrior
答案顯然具有可二分性,關鍵是我們無法知道字典序剛好為 \(mid\) 的子段是誰,也無法比較它和其它子段的字典序
於是我們隨機一個子段作為基準,列舉其它子段開頭為 \(l\),則如果 \([l,r]\) 比它字典序小則 \([l,r'](r'>r)\) 也比它字典序小,同理 \([l',r](l'<l)\) 也比它字典序小
雙指標維護 \(l,r\),維護線段樹表示區間內每種數的個數,每次移動即可線段樹上二分
時間複雜度 \(O(n\log n\log )\)
但隨機的 \(\log\) 常數較大,據說能被卡(
10.3
D. 小D與聖誕樹
一開始沒看到初始所有顏色不同,發現不會打暴力,由於只有修改到根鏈上的顏色為新顏色,因此每種顏色的點能組成一條路徑
考慮點 \(u\) 到根鏈上的顏色數 \(f_u\),即考慮多少個點與父節點顏色不同,這樣路徑查詢也可以變成 \(f_u+f_v-f_{lca}\times 2+1\)
路徑修改考慮樹鏈剖分,在每條重鏈上相當於區間推平,可以用 set
維護連續段
查詢子樹中 \(f\) 的最大值,可以在維護連續段的過程中,用線段樹維護 dfs 序一段區間 \(f\) 的最大值,要支援區間加減,區間查詢,每次修改顏色時,先去掉原來的段的交界處對子樹 \(f\) 的貢獻,再加上新的
根據顏色段均攤,總共只會增加 \(O(q\log n)\) 段,複雜度為 \(O(q\log ^2 n)\)
10.6
A. CF1503E 2-Coloring
這種題肯定考慮刻畫合法方案的形態,發現藍色格子是上下兩個單峰形狀,有兩種情況,一種是兩個峰不挨著,另一種是上面峰的最左端緊靠著下面峰的最右端,或反過來上面的最右端靠著下面的最左端
第一種情況,上下獨立,直接分別列舉上下中最大值最靠右的,最靠左的,不挨著就行,不是峰的位置就形如不降子序列的計數,最後字首和最佳化一下
第二種情況同理,直接列舉交界的格子,用組合數算方案
注意對稱性,兩邊如果只列舉上最靠右下最靠左記得乘二
賽時怎麼只記得把第二種情況 \(\times2\) 啊,還死活看不出來啊
D. 棋盤
賽時看到矩陣乘法最佳化,直接一眼鑑定為轉置原理,然後不會求逆,最後發現由於它的變換不是形如「某一行全部加到下一行」這種有先後順序的操作,可以看作把矩陣複製一遍,再雜亂地執行很多次把原矩陣的一行加到新矩陣的某行這樣的操作,無法用轉置原理去求逆
好像有人告訴我本質原因是轉移矩陣不是初等矩陣?不知道啊(
不過分析詢問的性質,可以看作是先預處理處整個棋盤,詢問的區間左端點,右端點均不降
轉移矩陣中只有 \(O(n)\) 個非零元素,可以 \(O(n^2)\) 完成一次乘法
每次彈出左端點,如果維護了字尾資訊就能直接做,加入右端點,維護了字首資訊就好了
於是用兩個棧,左邊棧維護從棧底位置開始的字尾資訊,右邊維護從棧底開始的字首資訊
唯一的問題是如果左邊棧空了,就無法繼續彈出
那暴力把所有元素放到左邊,發現這樣每個元素最多被重構一次,複雜度均攤還是 \(O(qn^2)\)
我只不過多存了一行的資訊,導致轉移矩陣大小 \(\times 4\) 既被卡時間又被卡空間,後來發現這種做法只需要把前一個、前兩個轉移矩陣都乘一下,代表從前一行、前兩行轉移,加起來就好了,分界點處維護相鄰兩種分界點……
10.21
B. P10093 [ROIR 2022 Day 2] 禮物
列舉第 \(k\) 大的數是多少,把 \(\ge\) 它的數看成 \(1\),則區間內包含 \(k\) 個 \(1\)
從大到小加入數,每加入一個數就處理包含它的區間
那麼每次只需處理 \(O(k)\) 個區間,然後用 ST 表維護區間最值,連結串列維護每個數的前面、後面一個已經加入的數
複雜度 \(O(nk+n\log n)\)
D. QOJ 6473 Cat and Mouse
直觀的理解是 Cat 開始在 \(1\) 號點,Mouse 開始在 \(x\),Mouse 先移動,要操作 Cat 的移動,在儘可能少的步數內使得 Mouse 無路可走
可以樸素設計 \(f_{x,y}\) 表示 Mouse 在 \(x\),Cat 在 \(y\) 時最少移動步數,加上轉移均攤複雜度為 \(O(n^2)\),據說設計 A* 跑 BFS 可以透過?
發現 Cat 不在 Mouse 的相鄰點或不是它下一步走的點時,它根本不會影響 Mouse 的移動,而且此時 Mouse 不會自己停止,設狀態二元組為 \((x,u)\),所以只有 \(F(x,x)=u\) 時是關鍵的,注意狀態中 Mouse 即 \(x\) 先走,初始為 \((X,1)\)
這之後 Cat 一直會貼在 Mouse 的旁邊,否則重新讓 Mouse 自由移動不優,就有兩種情況:
- Cat 在 \(u\),一直跟著 Mouse 移動,\((x,u)\to (F(x,u),x)\)
- \(u\) 在 \(x\) 的移動方向上,使 \(x\) 轉向,Mouse 移動至 \(w=F(x,u)\),目標狀態為 \((x,w)\)
剩下的問題是構造一種步數最少的方法,首先存在方案的條件是 \(F(w,w)=x\),否則 Cat 追不上 Mouse,Cat 先在 \(u\) 不動,下一步 Mouse 到了 \(F(w,u)=x\),Cat 再到 \(x\),到達狀態 \((x,x)\)
分類討論 \(F(x,x)\) 的取值,根據上面的情況它只有兩種可能:
- \(F(x,x)=w\),此時最優情況下 Cat 移動到 \(w\),到達 \((w,w)\),然後不動即可到 \((x,w)\),加上前面共花費 \(4\) 步
- \(F(x,x)=u\),會到達 \((u,x)\),但 Mouse 本來就是從 \(u\) 走過來的,讓它返回不如早攔截,且若 \(u\) 不是第一格可以實現在早一格攔截,否則直接換一種相鄰狀態更優,捨去
於是把關鍵狀態之間連邊,跑最短路,點數和邊數都是 \(O(n)\) 的,可以直接做也可以用桶維護最短路長度
最後列舉走了 \(i\) 步第一次到達關鍵狀態,由於之前 Mouse 的移動不受限制,可以確定它的位置 \(p\),列舉鄰居即 Cat 的位置 \(q\),此時若 \(q\)
為 \(p\) 的父節點則 \(dep_q-1\) 步走到即可,否則為了保持和 Mouse 之前錯開,需要 \(dep_q\) 步
好像樸素列舉鄰居雖然跑的很快但複雜度有點問題,不過這裡應該就能隨便最佳化了(
總複雜度為 \(O(n)/O(n\log n)\)
10.22
C. 歌劇演員
首先設計 DP,觀察如果固定了某個空隙中的 LIS 對答案的貢獻是哪一段,即要接在它前後哪兩個數中間,則空隙中儘量使這段 LIS 最長最優
而且如果確定每個空隙的貢獻,則它們產生貢獻的值域不交,可以統計貢獻
設第 \(i\) 個數前面有 \(a_i\) 個空隙,值 \(\le i\) 的有 \(s_i\) 個沒確定位置,\(f_i\) 為序列第 \(i\) 個確定的數是 LIS 末尾時 LIS 最長長度
拆開 \(\min\),即強行欽定一個更小,則轉移是三維偏序,用 CDQ 分治最佳化即可
10.23
啥逆天 4h 場三個 DS 一個神秘數數……
B. P7172 [COCI2020-2021#3] Specijacija
我賽時想出了大力 \(O(n\log^2n)\) 維護法,就是關注每個點在第 \(i\) 層是從左往右第 \(x\) 個,每個 \(a_i\) 相當於一次操作,若 \(x>a_i\) 則 \(x\gets x - 1\)
顯然一次操作代表的變換是個只有兩段的分段函式,\(n\) 個操作是有 \(O(n)\) 段的分段函式,大力使用線段樹維護區間 \([l,r]\) 內的操作複合後得到的分段函式
每個節點只用儲存哪些點處後面的函式值要 \(-1\),相當於差分,建樹時再用一棵線段樹維護函式每個位置的取值,要線段樹上二分找到第一個 \(>a_i\) 的位置,之後字尾減 \(1\),處理完後撤銷操作就避免了重新建樹
查詢時二分找出前面有多少個 \(-1\) 處即可得到經過區間內操作後的值
先查詢一次,將 \(x,y\) 拉至同一層,然後在維護操作的線段樹上二分,找到 \(x,y\) 經過變換後第一次相同的位置即是 lca
賽時最後的線段樹二分沒及時剪枝變成 \(O(n\log^3n)\) 了,賽後改對了,LG 上喜提最劣解(
官解是注意到分岔點只有 \(O(n)\) 個,只需要求出葉子構成的虛樹的樹,把每個點放到對應的鏈上,求對應點的 lca
從下往上可以維護出每層每個點所屬的鏈底部節點,每層相當於下一層只做了刪除一個數,修改一個數,還要支援查詢第 \(a_i\) 個,由於強制線上使用主席樹維護,注意特判兩個點在同一條鏈上的情況,lca 是深度更淺的點,複雜度 \(O(n\log n)\)
D. QOJ5020 舉辦乘涼州喵,舉辦乘涼州謝謝喵
先考慮 \(y\) 為 \(x\) 祖先的情況,這種去掉一個子節點的計數一般先考慮重鏈剖分,設 \(g(x,d)\) 為 \(x\) 除重兒子子樹外 \(x\) 子樹內距離 \(x\) 為 \(d\) 點的數量,\(f(x,d)\) 為 \(x\) 子樹內距離 \(x\) 為 \(d\) 點的數量
那麼答案是路徑上點的 \(g(\star,d)\) 之和,再特殊處理路徑上輕重鏈切換處的答案,是新重鏈底部的點 \(u\) 的 \(g(u,x)\) 加上它重兒子的 \(f(son(u),d-1)\) 減去路徑上前一個點即上一條重鏈的鏈頭 \(v\) 的 \(f(v,d-1)\)
子樹外的貢獻則是對 \(y\) 做鄰域數點,去掉 \(f(y,d)\) 即可,鄰域數點使用點分治,維護每種深度點數量的字首和即可 \(O(n\log n)\)
考慮計算 \(f(\star,d)\),顯然詢問有 \(O(q\log n)\) 次,離線到每個點上使用啟發式合併容易做到 \(O(n\log^2n)\),但這裡的資訊與深度有關,考慮長鏈剖分,維護每種深度點數量的字尾和,就能方便地完成在長鏈前端增加一個數和查詢的操作,複雜度瓶頸為查詢,\(O(q\log n)\)
最後則是一條路徑上 \(g\) 的和,由於所有節點輕子樹大小為 \(O(n\log n)\),將詢問拆成兩條到根的路徑相減,dfs 的過程中直接暴力加入當前點輕子樹,回溯時撤銷就得到了到根路徑上 \(g\) 之和,複雜度 \(O(n\log n)\)
於是整道題複雜度 \(O((n+q)\log n)\)
10.24
C. 換貓
首先我不知道需要知道結論,如果按順序依次給每種字元交替賦值為矩陣 \(A_c,A_c^{-1}\),則區間合法的充要條件是區間矩陣乘積為 \(I\)
給每種字元隨機一個可逆矩陣即可,這裡取 \(2\times 2\) 正確率就足夠了
每次交換的兩個位置 \(l,r\) 會導致 \(l,r\) 處矩陣產生改變,\([l+1,r-1]\) 內矩陣變為原來的逆矩陣
設矩陣字首積為 \(pre_i\),字尾積為 \(suf_i\),將矩陣變成原來的逆矩陣後的字首積是 \(pre'_i\)
則交換後序列乘積是 \(pre_{l-1}\times A_r\times (pre'_l)^{-1}\times pre'_{r-1}\times A_l\times suf_{r+1}\)
列舉 \(l,r\) 分別對應的字元,則 \(l,r\) 剩下的矩陣乘積可以分開,維護每種矩陣出現次數,用雜湊值儲存即可
時間複雜度 \(O(|\Sigma|^2n\log n)\)
D. 愛麗絲貓
環上並不好做,考慮斷開的位置,發現最大值比較關鍵,它所在的段的兩邊是獨立的,考慮這個環的形態,如果欽定最大值後面接順時針方向的數,則它逆時針方向的一段可以看作去除不選
因此把最大值放在序列最前面,逆時針、順時針把環拉成鏈各做一遍,取最大值即可
然後設 \(f(i,j)\) 表示前 \(i\) 個數分成 \(j\) 段的最大值,維護單調棧,可以用線段樹維護最大值做到 \(O(nk\log n)\)
做到 \(O(nk)\) 可以繼續對這個東西最佳化,維護這個最值可以用連結串列維護差分配合並查集找到下一個在連結串列中的數,做到 \(O(nk\alpha(n))\)
重新考慮單調棧,單調棧中的數單調遞減,將它抽象為樹結構,每個點的父節點是將它從單調棧中彈出的點,樹是動態構建的,此時單調棧中的每個點都作為根
則以點 \(x\) 為新劃分割槽間左端點時,新區間字首最大值個數是 \(x\) 到根的距離,字尾最大值個數為 \(x\) 的根到單調棧頂部數的個數
這些最大值可以在單調棧彈棧、入棧的時候維護,具體地,維護單調棧中 \(x\) 子樹中 \(h1_{x}=\max \{g_y + dep\}\),\(h2_x=\max g_y\),單調棧上字首 \(f1_x=\max\{h1_y\}\),\(f2_x=\max\{h2_y\}+dis_x\),其中 \(dis_x\) 為 \(x\) 到棧頂距離,複雜度 \(O(nk)\)