1. 程式人生 > 實用技巧 >CF204E - Little Elephant and Strings 題解

CF204E - Little Elephant and Strings 題解

考慮 SA,把串們用互不相同的分隔符連起來然後求。

很自然的想到,列舉每個串的每個位置(按照 SA 的順序)作為左端點貢獻。那麼答案顯然是所有串與它的最大字尾 lcp 中第 \(k\) 大的。而某個串與該字尾的最大字尾 lcp 怎麼求呢,顯然只可能是左右兩邊第一個屬於該串的字尾到該位置的 \(hi\)\(\min\),因為再往兩端擴充套件是單調的,這是 SA 的一個常用的性質。

如果該位置等於 \(1\) 的話,那就不可能往左邊,那就很好辦了,就找右邊第一個包含 \(k\) 個不同串的位置然後貢獻就可以了,決策是唯一的。但是普遍情況下,每個串是有兩個位置可以選的。對於每個位置都對每個串算出貢獻然後排序嗎?這就不會維護了。

我自己 yy 出來了一個線根對的方法。考慮根號分治,小串預處理,大串實時列舉。預處理的話,就在 SA 中的每兩個相鄰位置之間的變化量是和該串長度成線性的,所以線根;實時列舉就比較簡單了,也是線根;還要用一個 set 維護 \(k\) 大值。非常難寫,常數也很大,沒寫了。

事實上遇到這種難維護的東西,可以換一個角度思考。我們不看單串們這種瑣碎的東西了,我們考慮往兩端擴充套件到哪裡。那麼隨便推一推就會發現,對於每種往兩端擴充套件的方案,貢獻是兩端之間的 \(hi\) 的 RMinQ。然後就把所有方案給 \(\max\) 起來即可。

不難發現,最優的方案一定恰好包含 \(k\) 個不同的串,容易反證。我們考慮預處理出來這些區間們,然後很簡單的差分一下 multiset

掃一遍。但是又發現,這些區間的個數很容易被卡到平方,例如 \(1\to n,1\to n,1\to n\)。我們考慮對這種情況該如何解決。

  1. 該位置在中間的話,那顯然左右兩段各伸出去 \(0\) 是最好的;
  2. 在兩邊兩段的話,那顯然是另一段伸出去 \(0\) 最好。

綜上可以得出結論,我們只需要預處理那些,往左極小或往右極小的區間。這顯然是線性的,正反兩遍 two-pointers 即可。

code