四毛子算法

(日常口胡)

參考資料:luogu日報 LA 模板題解

一個能把 LCA,RMQ 和 LA 問題做到線性的奇怪技巧。
大概就是分塊,塊的大小爲 \(O(\log n)\),然後整塊用原算法,塊內可以直接枚舉所有可能情況(反正塊的大小隻有對數級)。

前置知識:ST表做 RMQ,歐拉環遊序 LCA 轉 RMQ,笛卡爾樹,長剖做 LA。

1. LCA

首先我們按照衆所周知的方法把問題轉成 RMQ。
然後你發現因爲跑的是歐拉序,相鄰的兩個數的差一定是 \(1\)\(-1\)
對於這樣一個數列做 RMQ 又叫做 \(\pm1\) RMQ 或 約束 RMQ。
然後我們就可以四毛子了!

令塊長爲 \(\dfrac{\log n}{2}\),那麼一共就分了 \(\dfrac{2n}{\log n}\) 塊。
整塊直接 ST 表就完事了,時間複雜度 \(O\left(\dfrac{2n}{\log n}\log\left(\dfrac{2n}{\log n}\right)\right)=O\left(\dfrac{2n}{\log n}(\log n+1-\log\log n)\right)=O(n)\)
你會發現塊長的 \(\log\) 直接把原來 ST 表預處理的 \(\log\) 給幹掉了。
然後我們再考慮塊內。
注意到相鄰的兩個數的差一定是 \(1\)\(-1\),我們作出差分序列,然後欽定 \(0\) 爲第一個數還原出一個序列。容易發現這樣並沒有改變數的大小關係。
因爲塊長 \(\dfrac{\log n}{2}\) 實在是太小了,這樣操作之後不同的序列一共只有 \(O(2^{(\log n)/2})=O(\sqrt{n})\) 種。
於是我們直接暴力枚舉,並且對於每一種序列暴力求出區間最大值並且存起來(沒必要用 ST 表了)。因此塊內時間複雜度爲 \(O\left(\sqrt{n}\left(\dfrac{\log n}{2}\right)^2\right)=O(n)\)
\(O(n)-O(1)\),四毛子算法大成功。

2. RMQ

我們已經有了快速的 LCA 做法,於是我們考慮將 RMQ 轉到 LCA 上來做。
操作是以數組下標和對應的值作爲鍵值建一棵笛卡爾樹,容易發現 \([l,r]\) 的 RMQ 就變成了 \(l,r\) 在笛卡爾樹上的 LCA。
\(O(n)-O(1)\) 完事。

3. LA

LA 問題處理起來就比較複雜了。但是和上面處理 LCA 的思路還是相似的。
首先大家知道長鏈剖分能做到 \(O(n\log n)-O(1)\),瓶頸在於樹上倍增求 \(2^k\) 級祖先。
我們對原來的算法做一些神奇的優化。我們可以預處理出每一個非葉子結點到子樹中任意一個葉子結點的距離(這個預處理顯然是線性的),然後我們只需要對所有的葉子結點做倍增就行了。
但我們發現葉子結點的個數最壞還是 \(O(n)\) 的。仿照之前的做法,我們考慮將一些葉子結點進行單獨處理,只留下 \(O\left(\dfrac{n}{\log n}\right)\) 個結點,這樣預處理就變成線性的了。

方法是這樣的:將子樹大小不超過 \(\dfrac{\log n}{4}\) 的結點都刪掉,容易發現刪去之後樹上的葉子結點只有 \(O\left(\dfrac{n}{\log n}\right)\) 個葉子結點,此時運用上面的操作,時間複雜度是 \(O(n)\) 的。
另外已經刪去的結點構成了由一些大小不超過 \(\dfrac{\log n}{4}\) 的森林。那我們只需要枚舉所有這樣大小的有根樹並對每種有根樹進行預處理即可。注意到這樣樹的種類數(實際上就是對應的 Catalan 數)大概是 \(O(4^{(\log n)/4})=O(\sqrt{n})\),最終對這些樹進行處理的複雜度就是 \(O\left(\sqrt{n}\left(\dfrac{\log n}{4}\right)^2\right)=O(n)\)

可以看出處理手法和 LCA 沒什麼大的區別,就是實現起來會複雜億點點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章