Comment on Problems
2024 March (Spark.md)
本部分是從古老文件
Spark.md
裡摘錄的,其餘的部分過於像流水賬,就不貼了原屬於三月的部分下午考題
P2573 [SCOI2012] 滑雪
注意到題目是求一個特殊有向圖的最小生成樹。
考慮 Prim 與 Kruskal 演算法的精髓,實際上是考察了所有可能擴大生成樹的轉移,所選擇的最小的一個可以擴大生成樹的轉移。
讓我們考慮直接按照邊權選取會發生的事情, 1-4-5
會被選擇,實際上,答案應該是 1-5-1
。這是因為,這種演算法並沒有考慮最下面那個節點的所有可能轉移,實際上最下面 1
的那條邊是不可以反向使用的,在正常的圖裡它可以擴大生成樹,這個圖裡不能。所以前面我們這個轉移是沒有考慮完善的,我們沒有考慮所有轉移就草率地做出了決策,這就有問題了。所以,MST貪心的正確性,就是在於已經考慮了所有可能轉移。
P4516 [JSOI2018] 潛入行動
是一道大揹包
但是比較關鍵的Trick就是要利用子樹的大小來限制轉移的上下界,揹包問題就是合併子樹的貢獻,跟啟發式合併類似的,利用大的不會多的原理保證了合併的複雜度。
P8575 「DTOI-2」星之河
關鍵思想是把子樹限制用DFN的大小關係來表達,然後轉換成偏序問題,使用 cdq分治解。
由於DFN序是隻有可能包含或者不交的,所以DFN大小的四維偏序可以轉換成三維
P2466 [SDOI2008] Sue 的小球
關鍵技巧是 費用提前計算 。應該在作出當前決策的時候把對後續決策的負面影響計算在當前決策的費用中,避免了難以表出損耗的問題。
以上就是所有的書中對該技巧的解讀。
我覺得,其實還不是能不能表的出這個問題,考慮暴力這個問題,在沒有加入時間到狀態時,你做的所有決策都是一個貪心式的純考慮當前最優不顧全域性最優的一種決策,之後你沒辦法用這種決策轉移,因為它很可能不是全域性最優的。把費用提前計算,就相當於加了一個狀態,把它壓到答案裡,相當於是貪心了,把各個狀態之間的干擾去掉了,比如我之前這個時間是要影響下一個轉移的代價的,現在我先計算了,下一個狀態可以看作是基於某個轉移之後所在的一個新起點開始轉移。對於DP,你子問題要有依賴關係,但是不能有干擾,就是要保證有最優子結構性質。
所以我認為不能叫做提前計算,因為這本來就是這個轉移帶來的代價,是後續要選擇這個轉移的代價,是在當前綜合了全域性的最優決策,這樣做保證了有最優子結構性質,所以這個Trick更像貪心(所以這個題加一個時間維應該也是有正確性的)。
P3224 [HNOI2012] 永無鄉
FHQTreap+啟發式合併
總結兩種可以把不可合併資料結構換成可並或者把不可線上資料結構換成線上的方法:
-
不可並換可並:啟發式合併,複雜度多一個log
-
不可線上換線上:二進位制分組
所以我們所說的暴力資料結構,就是在平衡操作次數和資料結構大小,而前者常與這個資料結構的數量有關(這令人想到根號分治),這種通常是元素數量是固定的時候才是對的(想想線段樹合併)。
關鍵就是:
大的不會多,小的不會大
P7150 [USACO20DEC] Stuck in a Rut S
本題是按照“拓撲序”模擬。
這樣就不必考慮已經轉移的狀態再被影響,這就是更新成環的情況。這個就是“無後效性”,其實上面第一題也有這樣類似的想法。
P4211 [LNOI2014] LCA
首先把所有的貢獻疊在一張圖上可以看出答案就是 \(path(i\to root)\cap path(z\to root)\)
在資訊可差分的時候,可以透過字首和避免從資料結構中刪除
P8903 [USACO22DEC] Bribing Friends G
這道題的關鍵在於要發現性質。。。
當問題困難的時候,思考透過列舉答案的方式轉換成判斷問題或者幫助發現性質。
可以發現如果確定了答案的集合,那麼考慮調整答案的集合能否更優,發現答案集合中 \(x\) 較大的如果用了冰淇淋就完全可以把冰淇淋用在 \(x\) 更小的奶牛上面置換出一個mooney,這樣既沒有改變mooney的總數又節省了一部分冰淇淋,所以就可以推出冰淇淋一定是集中使用在 \(x\) 更小的奶牛上。
P6005 [USACO20JAN] Time is Mooney G
這道題體現了DP與SSSP問題之間的關係。
首先時間肯定是非常重要的一個狀態(這點透過思考暴力容易得出),所以如果建成分層圖就是分出若干時間層,此時已經可以做了,然後轉成DP,也是自然的。
典中典 P2802 回家 和去年沒做出來的 P9751 [CSP-J 2023] 旅遊巴士
P3899 [湖南集訓] 更為厲害
題面amazing啊
線段樹合併的做法顯然。
用DFN序轉化為序列上問題,就得到了一個特殊的三維偏序問題,正如上面“P8575 「DTOI-2」星之河”的第一篇題解所說,DFN序的這種特殊的n維偏序是可以看作n-1維偏序做的。
P7300 [USACO21JAN] No Time to Paint S
本題中有貪心結論如下:
-
考慮最優方案是將整個區間先全塗上最小的顏色,然後再按照最小顏色分治
-
考慮證明:
-
如果不是先塗最小顏色,那麼還有操作可以選擇:
-
可以塗更淺顏色,這完全沒有用
-
可以選擇塗更深顏色,你不能跨越這些最小顏色的點
這時候的合法操作集是和上面最優策略操作之後的大小一樣的,選擇先塗最小顏色,這是具有決策包容性的
-
可以選擇塗這些最小顏色的點,那還不如直接塗一整個區間
-
證畢
-
-
故可以分前字尾來考慮
然鵝之前的 P4170 [CQOI2007] 塗色 ,就是沒有顏色深度限制的此題中,就沒有這個性質。
這是因為題目的限制不夠強,可能的轉移決策不止這些,就是說你是可以跨越最小顏色的點,就是不滿足了上面的決策包容性。仍然考慮可以節省操作次數的方法,就是要大段地塗上一個顏色把整個區間同一顏色的覆蓋,可以發現先給一段區間塗上一個底色是沒有影響的,之後答案不會變劣,還會變好,這裡就頗有一種DP的感覺,就像上面那個 “P2466 [SDOI2008] Sue 的小球” 中我說的這樣,這就是一種狀態,做出這個決策之後不影響之後做出的決策,所以又想到DP就是最佳化搜尋的列舉順序,我可以先塗一個底色然後再進入小區間塗色,這不影響,然後小區間塗色的答案又能夠組合成大區間的答案,只要我給他提供所有的可能性,列舉了所有的小區間劃分,就行了,還有要注意到無後效性,我最少就只要2個區間就能拼成一個大區間,如果分三個那就可以組合其中任意兩個,這就是為什麼區間DP裡面都是列舉斷點。這就是遞迴地分析。
寫不下去了,這個只能自行領會。抱歉用了太多逗號。
P3163 [CQOI2014] 危橋
其實這個交換兩個點的方法可以這樣理解:
-
第一次 \(a_1\to b_2\) ,錯誤的
-
第二次如果還是亂流,就是 \(b_2\to a_2\) ,那我就可以把第一次亂流的流量透過這條路徑送到正確的 \(a_2\) ,這樣不就行了嗎?你可能會覺得萬一路可以走的次數不夠呢?那顯然把兩條路徑合成一下就沒必要重複走一段路了,就是這樣:
P8900 [USACO22DEC] Barn Tree S
簡單貪心,顯然子樹內如果能夠自己補充自己那最好,基礎的思想就是儘量堆積到一定量之後再移動乾草。
P6573 [BalticOI 2017] Toll
可以發現每一次走一步肯定會到達下一層,所以要走幾步到達目標層的步數是固定的,走一步是可以到哪裡是可以知道的,這種一個大的轉移目標可以由若干小的決策步數拼起來的,就可以適用倍增最佳化,當然,如果空間允許,這個問題也可以在層數上分塊 😃
其他有趣的問題:
-
P1973 [NOI2011] NOI 嘉年華 分析見上文
-
P3435 [POI2006] OKR-Periods of Words
我們應該判斷一個字首的最短Border是否相交,但是KMP求出了最長Border
我們在此基礎上直接跳 Fail,然後記錄當前位置的最短Border長度,這樣做避免了重複遍歷同一個Border,這不禁讓人想到去年的提高T2 😦
2024 May/June
還是喜歡這種 All-in 式的東西,之後會考慮若干月之後更換這個文件
P9196 銷售基因鏈
AC自動機、Trie樹、二維數點
兩個轉換:
-
子樹資訊可以用 DFN 轉化為區間資訊,兩個區間限制進一步轉化為二維數點
-
把兩個字串拼起來同時匹配字首字尾(WTF),用 ACAM
警告:少用 std::string 的 + 進行字串拼接,非常慢(尤其是累加拼接,一定用 +=)
P3181 找相同字元
SA,字尾陣列
Md,怎麼又是拼接字串。
兩個字尾的 lcp(i,j) 等於 \(\min_{x\in(i,j]} height_x\) (就是中間最小值不包括左邊端點)
P3868 猜數字
CRT, 中國剩餘定理
這個轉化很明顯。不過要爆 long long 真的不友好。
P5319 [BJOI2019] 奧術神杖
分數規劃,二分,AC自動機,動態規劃
總結 |
---|
Observation:看到一對限制A/B,A變大B變小,反之亦然,可以抽象成取物品一樣的思路(這道題就是看到乘積變大的代價是多開一次根號),可以考慮推一下看看有沒有類似0/1分數規劃的二分條件,這題裡面,發現有,但是答案可能太大(有次方),可以考慮取對數,相當於把複雜度最佳化到精度上處理(取多了那你double的處理就需要高精度了,此時時間複雜度變成最佳化之前的);Warning:本題直接設狀態是不滿足最優子結構的(問題的最優解不能透過子問題的最優解得來),遇到這種不尋常的方程(非多項式)的時候不要想當然地做 |
理解 |
"【理解】對 SPFA 的理解:本題不能直接把操作放上 AC 自動機跑 SPFA,因為實際上可以把 SPFA 看作有環的 DP,Dij則是在特殊情況下用貪心進行的最佳化,然後有負權,最佳化失效的原因就是,當前的最優不一定是真最優(因為後面可能有很大的負邊權),SPFA 就說,沒關係,如果更優我就再進行更新嘛,但是這僅限於邊權是求和的操作,如果從當前的最優解(不論它是不是最終的最優解),都不一定可以推到下一個狀態的當前最優的話,那DP本身就是不能成立的,因為無法抽離出來子問題,無法用上一個子問題的答案推下一個問題的答案,下次一定要注意對根本的可行性的思考,不能純套模型" |
Trick |
這種特殊的限制(此消彼長型),可以考慮二分(其實不管怎麼都應該考慮有沒有二分性質)| 普通0/1分數規劃相當於是一條條向下的直線,這裡就是一些反比例曲線,但是隻要在定義域(此處為大於1實數)上單調 |
P3302 [SDOI2013] 森林
可持久化線段樹,啟發式合併,倍增
LCT 難以維護路徑 \(k\) 小值,如果沒有連邊,這個問題可以考慮使用 樹上主席樹 。而加上連邊,就要考慮維護樹上主席樹,可以直接暴力重構,考慮用啟發式合併思想。有點難寫。
P5675 [GZOI2017] 取石子游戲
線性基,動態規劃,博弈論,Nim 遊戲
動態規劃做法比較明顯。讓我們來欣賞 David_Mercury 的線性基做法。
我們必須清楚地知道,要處理 異或表出的問題,我們有什麼方法:異或線性基,01trie,dp
但是如果求方案數,我們往常都會想用 DP。不過,線性基也可以:
以下,令 \(S\subseteq \mathbb N\) 是數集,而 \(P\) 是 \(S\) 的線性基。設 \(\mathbf{consist}(x)\) 是對於 \(x\in S\) ,線性基內的陣列成 \(x\) 的集合。
-
\(\forall x\in S\), \(\mathbf{consist}(x)\) 是唯一的,所以要表示 \(x\) ,無論如何都不可能用到線性基裡面不在 \(\mathbf{consist}(x)\) 裡面的元素
-
還有的情況就是要選擇不線上性基裡面的元素,可以選擇那些不在 \(P\) 中的元素集合,這個集合的異或和,肯定也可以被線性基唯一表出,我們只需要在此基礎上選擇一些線性基裡面有的元素就可以消除他們的影響,再選一些 \(\mathbf{consist}(x)\) 裡面的數就可以湊出 \(x\)
以上兩種合起來就是 \(2^{|S-\mathbf{consist(x)}|}\)
這種方法頗需要一些構造性的思維和對線性基的理解,況且該演算法較大地最佳化了複雜度,是一種更為厲害的方法。
P6619 冰火戰士
倍增,樹狀陣列
這道題給我們貢獻了一個“樹狀陣列上倍增的技巧”
首先答案是單峰函式是很顯然的,對於這種函式,如果知道左右兩邊的解析式,可以把他們做差變成單調函式,然後找零點。
可考慮二分,但是每次二分是 \(\mathcal O(n\log n)\) 的,發現實際上每一次重複遍歷的許多點,考慮到樹狀陣列是基於倍增原理最佳化的,考慮樹狀陣列上倍增(注意必從 0 開始,要不然就不是 lowbit 了)
然後有一個注意點就是 0 點可能不是整數,要考慮從兩邊逼近 0 點,由於取得是最大的答案,大於 0 那邊逼近過後還要往右邊再跳,防止右邊有一段等值的區間
P9883 Fenwick Tree
思維
這道題其實是基於一種動態規劃的思想,考慮樹狀陣列子樹內部對它的貢獻,如果有直接兒子是 0 ,這棵子樹內就不能對根產生影響,如果只有一個是非 0,那麼肯定對根產生非 0 的影響,如果有兩個以上是非 0,那麼他們可以互相取相反數使得可以任意控制對根的影響,所以這種情況下不需要花費額外次數。
CF1083C Max Mex
線段樹,ST 表,LCA,思維
線段樹不一定非要維護一個序列,只要是可以合併的資訊都是可以的。本題的答案顯然滿足二分性,考慮暴力做,即遇到第一個不可以的點就輸出答案,發現如果可以連成鏈就一定可以,那麼就是一個一個往已經確定的路徑裡面加點,這是好實現的,然後呢??(然後就開啟了題解)(假裝是自己想出來的)注意到(還敢用注意到)修改的時候其實只會影響一部分路徑,如果可以分塊處理,把修改的點重新加入旁邊沒有被修改的塊,就可以避免重複建立路徑。所以這提示我們路徑其實也是可以合併的,這種可以合併的資訊,線段樹可以透過分塊維護它的構成,做到快速修改。
還有就是基於 ST表 的 \(\mathcal O(n)\) 預處理靜態 LCA,用 dfn 序把 LCA 問題轉換成 RMQ 問題,注意討論他們在一條鏈上的情況,可把查詢區間變成 \([dfn_x+1,dfn_y]\)
P9695 [GDCPC2023] Traveling in Cells
線段樹,二分
技巧是,給每一個顏色開動態開點線段樹,一起做線段樹上二分。
不過,這個題給我們一種好寫的二分方法,就是每一個區間的時候,檢查(以二分左端點為例):
-
這個區間是否能夠全部被放進答案裡,如果可以,返回左端點,不可以,繼續遞迴
-
如果右邊全部超出範圍,直接向左邊走
-
如果兩邊都沒超,先嚐試右邊,如果右邊返回可以繼續擴充,再嘗試左邊
這樣為什麼是對的:
如果第三步右邊向下繼續遞迴了,說明不可能向左邊擴充,否則只用檢查一層就會返回。
Gym104090B Useful Algorithm
思維,線段樹
首先求最大貢獻的一位,可以一個一個來處理之後取最大值
現在就是要知道怎麼把“會在第 i 位進位”表達出來,發現第 i 位及其之後的位對於這個問題沒有貢獻,所以考慮到進位制就是多項式,可以取模去掉後面的項——處理問題的時候去掉多餘的資訊總是好的,前面的項相加如果比 \(2^i\) 大就說明要進位。
那麼就是要查詢 \(\max_{a+b\ge 2_i}\{\max_{c_k=a}d_k+\max_{c_k=b}d_k\}\) 這個不好維護,由於 \(2^i\) 是常量,則可以 移項 變成一個偏序問題。則可以用線段樹維護 CDQ 分治完成(自己取的名字)。