最短路專項

spdarkle發表於2024-10-05

link

A -BZOJ3706

顯然可以發現有解當且僅當僅保留所有黑色邊時,每個連通塊存在尤拉回路

最小操作次數可以考慮將黑色連通塊縮成一個點,然後在原圖裡一個連通塊拿出任意一顆生成樹都可以將這裡面的黑點全部消掉(走到黑點的時候走尤拉回路,樹邊都只會經過兩次且都是白邊)。

顯然不存在比這個更小的解,所以答案就是包含黑色邊的連通塊個數。

這個維護是簡單的,可以透過並查集做到 \(O(n\alpha(n))\),但是你可以使用DFS直接弄到 \(O(n)\)

B-「聯合省選 2020 B」丁香之路

原題相當於是找一條尤拉通路包含所給邊且起點終點分別是 \(s,i\)

注意到 \(n\le 2500\),所以考慮平方演算法。

通路比較麻煩,可以先連上 \((s,i)\) 變成迴路。那麼顯然對給定特殊邊以及這條邊先合併連通塊,接下來對於奇度點顯然有偶數個,且需要想辦法合併,那麼顯然是數值上相鄰的奇度點兩兩合併。

這時候問題又來了,現在變成了若干個存在尤拉回路的子圖,每個子圖大小不小於 \(2\),我們需要將其連線成一個尤拉回路。

將這些連通塊看成一個點,變成尤拉回路的話,最優方案顯然是原圖中它們的最小生成樹邊權和的兩倍(因為如果有 \(u\to v\to z\to u\),則根據最小生成樹的原則,我們會選取其中較小的兩條邊,那麼根據原題絕對值邊權顯然可以替代掉另一條邊),而可能構成最小生成樹的邊只可能是數值上相鄰但不在同一個連通塊內的點。

那就完了。

C-「AHOI2014」騎士遊戲

顯然建圖

發現 \(R\ge 1\)

處理一個dp :\(w_u=\min(K_u,S_u+\sum _{u\to v}w_v)\)

這很經典,由於所有值非負,所以將 \(K_u\) 扔進小根堆,更新其他點的 \(w\),當更新好了之後又將右邊的值加入小根堆更新。某個點第一次被取出的時候就解決了它的 \(w\),因為小根堆取出的值總是不降的。這時候如果物抗還沒有算出說明物抗不可能低於法抗

D-「BZOJ4070 Apio2015」雅加達的摩天樓

由於是由一隻狗傳遞訊息,所以不會出現兩隻狗互相跳來相遇的情況,也即任意時刻只會有一直狗在動。

一個比較暴力的轉移是設 \((pos,v)\) 為當前知道訊息的狗在 \(pos\) 處能力為 \(v\)

則當 \(v\le \sqrt n\) 時有 \(O(n\sqrt n)\) 的狀態,而對於 \(v\ge \sqrt n\)\(pos\) 只有 \(\sqrt n\) 種,所以狀態總數是 \(O((n+m)\sqrt n)\) 的。

注意到邊權全是 \(1\),直接廣搜即可。

閾值分治分析複雜度

E-「JOI 2020 Final」奧運公交

如果修改的邊不在正反圖的最短路徑樹上,則不會影響最短路,可以直接預處理四個狀態的最短路直接拼起來。

否則重新跑一邊算,這樣的邊僅有 \(O(n)\) 條。

F-「NOIP2017」逛公園

注意到 \(K\) 很小可以直接考慮DP。

如果有零環且可能被經過則無窮。

先處理最短路,那麼走邊 \((u,v,w)\) 相對於走最短路額外走了 \(w-(d_v-d_u)\) 的路。

所以設 \(f_{u,k}\)\(1\to u\),當前長度為 \(d_u+k\) 的方案數,直接轉移就好了。

轉移很簡單的,\(d_v+k'=d_u+k+w\implies k'=d_u-d_v+k+w\)

G-「GXOI / GZOI2019」旅行者

直接跑多源最短路就好了,注意到可能自己回到自己,所以記錄一個起點不同的次短路即可。

正解是二進位制分組,這挺有啟發,列舉每個 bit 位,將其 \(0/1\) 分組,可以在額外增加 \(O(\log V)\) 倍的時間複雜度內統計一整個集合兩兩之間的答案,要求可以快速計算兩個集合之間的答案

還有一個想法是正反圖跑dijkstra並染色,所要求的最短路必然不會經過第三個特殊點,且兩個特殊點之間的路徑存在一箇中邊,列舉它,左右端點的最優解不同,直接算進答案,顯然不會少算,也不會算到更優的非法解。

H-「COCI2017-2018#3」portal

直接暴力,要麼按部就班地走,要麼就可以花費到四個方向牆最短的距離走到四個方向任意一個牆的旁邊,列舉轉移即可。

I-「CEOI2014」The Wall

垃圾東西啊,這東西好像在哪裡見過

首先可以發現所有點到左上角的最短路都會被最優方案圍著,如果不是則用最短路替換掉顯然不劣。

問題變成找一條迴路包含所有的最短路徑。

我們把路徑畫出來,將兩個格子的十字交界拆為 4 個點,抄下圖

	(1)---(2)
	 | (0) | 
	(3)---(4)
	
       1|2   (x-1,y)   1|2
---------------------------------
       3|4             3|4
(x,y-1) |     (x,y)     |   (x,y+1)
       1|2             1|2
---------------------------------
       3|4   (x+1,y)   3|4

然後一條邊就被拆為了兩條路,方便迴路行走。

對於這樣的虛點連邊,不能夠越過原先的最短路徑,也不能夠使用在村莊範圍內的虛點,連邊可以由這個圖解釋,注意左上角連邊需要特判。

	(1)-0-(2)---w---(1)-0-(2)
	 |     |         |     0 
	(3)-0-(4)---w---(3)-0-(4)
	 |     |         |     |
	 w     w         w     w
	 |     |         |     |
	(1)-0-(2)---w---(1)-0-(2)
	 0     0         0     0 
	(3)-0-(4)---w---(3)-0-(4)

跑這樣的一個最短路就行了,它不會穿過任意最短路,也會包含所有村莊。

寫起來是真的臭

J-「JOISC 2020 Day4」治療計劃

時間軸很麻煩啊,能不能統一?感覺相當抽象

考慮一個基本的DP,設 \(f_{t,i}\) 為時刻 \(t\)\([1,i]\) 的人已經治好了的最小花費。

那麼我們可以列舉 \(T_i\le t\) 的任意決策進行順推,它決策之後可能會擴充套件這個區間。

這個顯然是對的,怎麼最佳化啊

\(t\) 大概是不必要的,我們只需要記錄當前的區間右端點是由誰貢獻的即可。

把狀態當作點,是個最短路,起點應當是 \(l=1\) 的,終點是 \(r=n\) 的。

按照 \(t\) 排序,也即 \(i<j\implies t_i\le t_j\),能夠接續貢獻的只有兩個邊:

\[\begin{cases} l_j+(t_j-t_i)\le r_i+1\implies l_j+t_j\le r_i+t_i+1 :i\to j\\ l_i+(t_j-t_i)\le r_j+1\implies l_i-t_i\le r_j-t_j+1 :j\to i\\ \end{cases} \]

如何最佳化建圖?

注意這些偏序都很簡單,直接CDQ排序之後做字首最佳化建圖即可

K-「HNOI2009」最小圈

二分答案判負環即可。

L-「SCOI2011」糖果

比較蠢的差分約束題。

首先差分約束建圖,然後 tarjan 縮點,直接拓撲排序即可。

如果一個SCC裡面的邊邊權非零則非法。

M-CF1521D

樹的最小剖分。

其實就是要斷掉最少的邊使得原樹變為若干鏈。

那麼可以轉化為儘可能保留更多的邊

那麼一個點最多保留兩條,且由於每條邊保留貢獻相同,能保留就保留。

所以直接貪心 dfs,如果當前可以合併兩條鏈直接合,多出的鏈就不管了,否則如果有一條鏈就傳給父親解決。

這顯然是對的。

N-arc121E

給定一顆有根樹,要求有多少個排列 \(p\)

滿足 \(p_i\) 不是 \(i\) 的祖先

你要求 \((i,p_i)\),可以等效為求 \((p_i,i)\),也就是說保證 \(p_i\) 不是 \(i\) 的後代這樣的排列也是可以的。

問題得到轉化,考慮容斥。

\(f_{u,k}\) 為子樹 \(u\) 欽定了 \(k\) 個位置非法的方案數,只考慮這 \(k\) 個位置的分配情況, \(F(i)\) 是欽定 \(i\) 個位置非法的方案數,顯然是 \(f_{1,i}(n-i)!\),答案顯然是 \(\sum (-1)^iF(i)\)

\(f_{u,k}\) 首先是做一個樹上揹包轉移,然後可以列舉取自己由 \(f_{u,k-1}\) 轉移。取自己的方案數就是 \(siz_u-k\)

O-arc121F

容易發現 and 0 和 or 1 的話另一半什麼情況是沒有意義的

進一步感覺先 and 會比先 or 優秀一點

發現把 and 先弄完再弄 or 是最優的

然後不合法當且僅當每個 and 連通塊都死了,容斥這一步,欽定每一個 and 連通塊都存在零即可,那麼只需要設 \(f_{i,0/1}\) 為解決完子樹 \(i\) 後當前 \(i\) 所在連通塊有沒有出現過 \(0\) 即可。轉移的時候列舉這條邊是 and 還是 or 就行了。

P-「COCI 2020.11」Svjetlo

先刪掉全亮的子樹。

顯然每棵樹最多會進去一次,最多會出來一次,那麼路徑端點就只能是有 \(0/1/2\) 個出現在子樹內。

而外部怎麼走與樹內部怎麼走無關,你走出去了再走回來就可以省掉走出去的部分看成子樹根節點走兩次。

所以可以設 \(f[u,0/1,0/1/2]\) 表示子樹 \(u\) 除了 \(u\) 全亮,\(u\) 的狀態是 \(0/1\),而路徑端點有 \(0/1/2\) 個在子樹內。

顯然第三維是個揹包的轉移。前面的轉移就相當噁心的分類討論。

一個核心是往返一次會改變兩個節點的狀態。

對於 \(0+0\) 的合併,根據最後兒子和根是走一次還是往返一次來判斷。

對於 \(0+1\) 的合併,先根據最後是走一次還是往返判斷,然後再根據起點在哪邊討論,一共四種情況

對於 \(0+2,1+1\) 本質不變。

最後你可以選擇當前點作為路徑的一個端點來更新(注意可以是迴路)。

Q-CF1442E

如果只有黑白點,那麼顯然是相連的同色點縮成一個點之後的直徑長度

也可以看作同色點之間距離是0

然後加上灰色無非就是兩色選一,設 \(f_{i,0/1}\) 為當前點是這個顏色時的最長鏈的最小,\(g_{i,0/1}\) 表示當前狀態下的直徑最小長度。

答案顯然是 \(\lceil\frac{\max(\min g_{i,0},g_{i,1})}{2}\rceil\)

R-CF512D

一個環顯然無法操作,而對於一棵樹而言就是一個拓撲序。

那麼一個點如果屬於某個環,這就是不動點。

如果一個連通塊都是樹,那麼沒有限制,只要滿足某種拓撲序

否則把有環的連通塊中可動點拿出來會組成一個森林,且有固定的根最後才能夠刪去(不可能有兩個根,否則兩個根路徑上的點都是環上點)。

先考慮固定根的情況,直接就是拓撲序計數,跑一個揹包即可。

對於一般樹的情況,需要想辦法去重。

不妨我們欽定根,且根不會被刪除且是沒有被刪除的點裡編號最小的,則編號小於根的所有子樹是刪完了的。

略微更改一下DP陣列即可(即遇到欽定刪完的就只保留刪完的狀態其餘全部清空)。

最後各個部分的答案就直接卷積即可。

相關文章