小向的試煉 Vol. 2 題解
hansonyu123
1 洞穴 (Cave)
相信大家都看過這類問題,只是通常是問一堆螞蟻在一條線段上走路,全部的螞蟻走 出線段要花多久。這個問題很簡單,因為只要把相撞的螞蟻當作是交換身份後穿越就好 了。然而這題因為要找出最後走出去的螞蟻是哪一隻,所以「交換身份」這步就需要再多 做處理了。
1.1 Subtask 1
直接模擬螞蟻們在洞穴內的移動狀況即可。複雜度O(N L)(因為至多只需要L秒就可 以走出去了)。如此可以得到12分。
1.2 Subtask 2
經過一些實驗後不難發現答案只跟向右的螞蟻數N1 以及向左的螞蟻數N2 有關,而 跟他們之間的位置無關。再經由觀察可以發現最後從右邊走出去的螞蟻是從右邊數過來第 N1 隻螞蟻,最後從左邊走出去的螞蟻是從左邊數來第N2 隻螞蟻。因此只需要看最後一隻 螞蟻是從右還從左出去,而這可以用交換身份後穿越這個老方法直接判定:對每隻螞蟻,
都只考慮他,看看他需要花多久走出去,再看花最久走出去的螞蟻是朝右還朝左就好。複 雜度O(NlogN)(排序)或O(N)(nth_element)。如此可獲得12分。詳細證明見Subtask 4的 作法。
1.3 Subtask 3
N ≤ 103,模擬的方法可能還行得通。然而一次走一格是行不通的。注意到螞蟻碰撞 到的次數最多O(N2)次,如果每次找出下個相撞的螞蟻以及更新所有人的位置只需要花
I
O(logN)的時間的話就可以用O(N2logN)的時間解決問題。因為我們在意的是相鄰兩個 螞蟻之間的距離,所以可以考慮a1, a2, . . . , aN−1,其中ai 代表左邊數來第i隻螞蟻及第 i+ 1隻螞蟻的距離。除此之外,我們還在意每過一秒,每個值的變動應該要是多少。我們 可以利用b1, b2, . . . , bN−1 記錄。
接著就是轉換問題。首先,我們需要找到離下次碰撞還剩多久。容易知道這個時間長 就是min{ai|bi =−1}。假設這個時間是aj =T,那麼就將ai加上T ×bi。接著因為第j隻 螞蟻以及第j + 1隻螞蟻相撞必須轉向,所以bj−1 減少1, bj+1減少1, bj = 1。而我們希望 能在O(logN)的時間完成如此一次操作。
不難想像線段樹懶人標能達成要求。對於[l, r)節點,儲存一個懶人標代表該節點尚 未處理的時間長度t,以及min{ai|bi = −1, i ∈ [l, r)},min{ai|bi = 0, i∈ [l, r)},min{ai|bi =
1, i∈[l, r)}三個值。那麼時間長可由區間詢問得知,ai 加上T ×bi 可使用懶人標,而改變
bj−1, bj+1, bj 則是三次單點修改。單次操作複雜度O(logN),故總複雜度O(N2logN)。如 此可獲得48分。
1.4 Subtask 4
試圖證明Subtask 2中的發現,可以獲得整題想法的啟發。以下提供兩種方法。
第一種想法是觀察N1 = 1 的情況。觀察過程,可以發現對向左走的螞蟻來說,向右 走的那隻螞蟻看起來就像是開始了一個「擾動」,而這個擾動不斷地傳到隊伍尾端。對向 左走的螞蟻隊列D來說,最後D的前端多了原本向右走的螞蟻,而少了尾端。
如果左邊有很多隻螞蟻向右走,那麼整個過程不再像只有一隻螞蟻般單純。然而因為 螞蟻都是等速前進的,所以兩個「擾動」之間不會互相影響。因此不妨將所有向右走的螞 蟻造成的影響逐次計算:每次都推入D的前端,將D的尾端拔除。如此便可證得Subtask 2中的觀察。
由上面的想法可以給出下面的算法:先對所有螞蟻的座標排序,再由右開始維護向走 左的螞蟻隊列D。如果掃到的螞蟻向左,推入D的前端。如果向右,那麼推入D的前端,
再將D的尾端拔除。由此可以得到向左走出最晚的螞蟻(即最後D的尾端)。向右也可以同 樣處理。複雜度O(NlogN)。
另一個想法是:不難發現螞蟻之間不會交換順序,而最終狀態很好求出:所求的螞蟻 一定是由左數來第N2隻螞蟻或由右數來第N1 隻螞蟻。因此排序或使用nth_element後便 可得出所求的螞蟻。複雜度O(NlogN)或O(N)。也就是說Subtask 2的解法如果沒有加上 特判的話是可以獲得100分的。
II
2 平行世界 (Parallel universe)
其實每個平行世界連接的魔力隧道最多只能被縮短2條的意思就是將樹上的一堆不相 交鏈縮短。然而採用前者的定義思考對此題較有幫助。
2.1 Subtask 1
最直接的作法是枚舉每個邊要不要被縮短,再用樹分治/樹DP判斷該次的方案是否合 法以及最大共享難度。複雜度O(N2N)。如此可得12分。
2.2 Subtask 2
將樹上的一堆不相交鏈縮短,直覺上最大共享難度應該會非常小。因此我們可以試著 估計最大共享難度最小值的最大可能值。
因為縮短的邊會構成很多不相交的鏈,所以一個自然的想法是如果採用「重鏈剖分」
的話答案會是多少呢?也就是說,對於每個點,都縮短他到最大子樹之間的邊。容易發現 這樣縮短邊是符合題目條件的。除此之外,對於任一個點,他到祖先的路徑中,每經過一 條沒被縮短的邊就代表當前節點的子樹大小至少翻倍。因此沒被縮短的邊數= O(logN)。
這告訴我們從0開始測試最大共享難度是該數的方案有多少個,直到為非零為止是一個負 擔不會太大的作法。如此便可嘗試樹DP。(事實上最大值發生在奇數層有一個兒子,偶數 層有兩個兒子的樹。)
在進行樹DP時,枚舉根附近被縮短的魔力隧道方案個數。假設目前要算的是最大共 享難度最小值是n 的方案個數dp[n][0]。對於根的每個小孩s,如果他的父邊沒被縮短了,
那麼他對這個情況的方案個數貢獻會多乘一個dp[n−1][s]。如果父邊被縮短,那麼要考慮 的是s的其它邊最多只能再縮短一個小孩的方案個數。為此,引進dp1[n][s],代表以s為 根且s往下只能被縮短一條邊時,最大共享難度最小值是n的方案個數。
找出所有所需的狀態後,餘下的只有列出轉移式。不難看出:
dp1[n][s] = ∏
s1是s的小孩
dp[n−1][s1] + ∑
s1是s的小孩
dp1[n][s1] ∏
s2是s的小孩,s2̸=s1
dp[n−1][s2]
dp[n][s] =dp1[n][s] + ∑
s1,s2是s的小孩,s1<s2
dp1[n][s1]dp1[n][s2] ∏
s3是s的小孩,s3̸=s1,s2
dp[n−1][s3] 故假設dp[n−1][·], dp1[n−1][·]均算出時,算出dp[n][·], dp1[n][·]的複雜度只有O(N3)。因 為n的最大值只有O(logN),所以總複雜度O(N3logN)。如此可獲得28分。
III
雖然如此的作法只能得出O(N3logN)的作法似乎有點令人氣餒,但是接下來的目標 非常清晰:只需要快速地算出兩個sigma的值即可。Subtask 4以及Subtask 5分別採取了 兩種不同的方法計算。
2.3 Subtask 3
在不知道答案大小為O(logN)的情況下使用Subtask 5 的DP方法即可得到O(N2)的 作法。如此可得48分。
2.4 Subtask 4
這個作法用到了1000000007是質數的事實。
假設小孩的dp[n−1][·]乘積X模1000000007不為零,那麼將兩個sigma提出X後只
需要計算 ∑
s1是s的小孩
dp1[n][s1]dp[n−1][s1]−1
以及 ∑
s1,s2是s的小孩,s1<s2
(dp1[n][s1]dp[n−1][s1]−1)(dp1[n][s2]dp[n−1][s2]−1)
前者可用 O(小孩個數) 算出,而後者可藉由和的平方扣掉平方和除以2得到,也就是
O(小孩個數 )。因此再考慮計算模逆元的複雜度,總複雜度為O(NlogNlogP),其中
P = 1000000007。
然而若X為零,就不能再提出X 了。不過在這個情況,不難發現需要求的兩項中有 很多消為零。比如說如果恰一個dp[n−1][s1]是零,那麼第一坨只剩一項,而第二坨則化 約成前一種狀況(X ̸= 0)的第一坨。如果有兩個是零,那麼第一坨是零,第二坨只剩一項。
如果至少有三個是零,那麼全部都是零。因此不管是哪種狀況,都可以用O(小孩個數) 的複雜度計算出來。加上模逆元的複雜度,總複雜度為O(NlogNlogP)。如此可以獲得 72分。
2.5 Subtask 5
Subtask 4的算法的問題只有計算模逆元過於耗時。注意到Subtask 4的作法中,計算
兩坨的方法類似於DP的精神,故可考慮使用DP求解。而這個算法不依賴於模的數是否 為質數。
IV
想法非常簡單。令s的小孩為s1, s2, . . . , st且dp′[i][j]代表前j 個小孩中選i個,令他 們的貢獻為dp1[n][·],剩下的貢獻為dp[n−1][·]相乘後再對所有選法加起來的值。可見所 求的即是dp′[0][t], dp′[1][t], dp′[2][t]。轉移式也不難寫:
dp′[0][j] =dp′[0][j−1]×dp[n−1][sj]
dp′[i][j] =dp′[i][j−1]×dp[n−1][sj] +dp′[i−1][j−1]×dp1[n][sj](1 ≤i≤2) 對於一個節點,複雜度為O(小孩個數),故總複雜度為O(NlogN)。然而空間複雜度也
為O(NlogN),會MLE。不過不難發現可以對第一維滾動,所以空間複雜度降為O(N),
此題便得到了完整的解決。
事實上,因為模1000000007是0不代表沒有方法,所以還需要一個bool陣列求是否 存在一個合法縮短的方法。這個bool的DP方法是完全類似的,這裡不再贅述。不過因為 測資構造困難,本題測資裡並沒有這種測資。
3 轉 (Rotate!)
首先不難觀察到所有貪婪的換法都是最佳換法。換句話說,每次挑定一個不在位置上 的球,把該顆球換到他所應該在的位置,持續做下去一定是最佳策略。然而證明這個需要 費點口舌。沒有興趣的可以直接跳到Subtask 1的解決方法。如果對置換群略有研究的話應 該知道這是個顯見的事實所以也跳到Subtask 1吧。
先來看一個常用的事實:
引理1. 對於1到N 到任何一個排列,如果存在兩種交換的方案(規定每次都一定要換相 異的數),並且分別用了n1 以及n2次交換,那麼n1, n2 同奇偶。
證明: 考慮排列的逆序數對個數。不難證明對於每次交換,逆序數對個數的奇偶性一定改 變。因此n1, n2 都和原排列的逆序數對個數同奇偶(注意到升冪排列的逆序數對個數為0),
也就是說n1, n2 同奇偶。
接下來的這個事實看起來很顯然,不過需要稍加說明。
引理2. 對於1到N 的某個排列,若i已在正確的位置上,且某個方案用了n次的交換換 回原位並且i被換過,那麼存在一個交換至多n−2次的方案。
證明: 首先,對於原本的方案,假設有相鄰兩次交換是先交換 a, b再交換c, d且{a, b} ∩ {c, d}=ϕ,那麼先交換c, d再交換a, b的效用是一樣的。假設{a, b} ∩ {c, d} ̸=ϕ,不妨假
設a=c,那麼先交換b, d再交換a, b的效用是一樣的。不論如何,都可以藉由這種交換交
換的次序將所有和i有關的交換往前挪。
V
假設和i有關的交換有k個,那麼有和i交換過的數至多只有k個,所以由貪婪的換 法知道存在一個至多只換k−1次的方法將這些數換到原本k個交換後的結果。由引理1 知這個換法的奇偶性和k 一樣,所以至多只換了k−2次。將一開始的k 次交換換成這至 多只換k−2次的交換,那麼因為除了 i之外的其它數在兩方案的位置都相同,所以i也 在原位。因此我們成功地節省了兩次交換,也就證明了這個命題。
利用引理2可以證出原本的宣稱。假設已有一個最佳方案A,我們需要證明對於一個 不在原位的i存在一個最佳方案B 使得第一次交換將i換回原位。假設他所應該在的位置
上的數是j,那麼先交換兩次i, j 再開始使用最佳方案A的換法。考慮第一次後的所有交
換,因為此時i已在本來的位置上,所以可以節省至少兩次交換。因此得到的新方案B 不 會比原本的方案A還差,也就是說新方案B 也是最佳方案。如此一來貪婪法的正確性便 得證。
3.1 Subtask 1
直接模擬貪婪的過程,每次都看看N 在不在位置上,不在的話就換到位置上。N 遞 減後繼續貪婪。複雜度O(N)。如此可獲得8分。
3.2 Subtask 2
不難發現每次題目被交換一次後答案只會增加1或減少1。故這實際上是5題是非題,
可以對輸入hash後二分搜。複雜度O(N)。如此可獲得20分。
3.3 Subtask 3
其實根本可以每交換一次之後當作新問題解決。複雜度O(M N),32分。
3.4 Subtask 4
會不斷被改變位置的人其實有限。如果我們用好的貪婪順序或許可以得到好結果。
對於N,看看他的位置上是誰。假設是i,再看看i的位置上是誰。一直這樣找下一 個人,直到找回N 為止。可以對這些人貪婪,發現需要花(個數)−1次才能將這些人換回 原位。對其它人也這樣做,便可以發現答案就是N−(圈的個數)。所以我們只需要知道圈 的個數之變動即可。
VI
注意到因為只會換到前面的104個人,而整體的圈個數變動也就是只考前104 個人的 圈個數變動,故可以將前104 的人用Subtask 3的作法找出答案是+1還是-1,再更新真正 的答案。複雜度O(N +maxi×M)。如此可獲得56分。
3.5 Subtask 5
不難觀察並證明出答案+1若且唯若所換的兩個球在不同的圈,而答案-1若且唯若兩 個球在一樣的圈。而且如果兩球分屬不同的圈,那麼交換後兩個圈會合併。如果兩球在同 樣的圈,那麼交換後圈會依兩個球的位置分裂成兩個圈。為了支援分裂和合併,看起來
treap是最適合的工具。
現在對每個圈都用一個treap代表。合併時需要先將兩個圈「轉」到正確的位置。而
「轉」到正確的位置就是將treap從選到的球分裂成兩個treap,再將兩個treap逆序黏合。分 裂時需要將分裂的圈「轉」到正確的位置再分裂。看起來treap的確支援這兩個操作。不過 有幾個小小的問題。首先,球並沒有二分搜結構,我們不能依球的編號分裂treap;再者,
我們需要快速得知兩顆球所在的treap是哪一棵。一個相當簡易的作法是讓所有的節點都 記錄其祖先以及子樹大小。如此一來,便可以邊往上走找到祖先邊記錄所要的球是從左邊 數來第幾顆球。這些實作細節都相當容易,這裡不再多做著墨。
可見每次詢問都只需要花O(logN)的時間解決。再加上初始化,總複雜度為O((N + M)logN)。
VII