HNOI 2019 簡要題解
沒想到自己竟也能有機會寫下這篇題解呢。
Day1T1 魚
枚舉\(AD\)兩點后發現\(BC\)與\(EF\)相對獨立,因此只需要計算合法的\(BC\)對數與\(EF\)對數,相乘即可。
先考慮計算\(EF\)的對數。確定\(AD\)后,滿足條件的\(EF\)對數即為在某個半平面內與\(D\)點距離相等的點對數目。枚舉\(D\)后若亂序枚舉\(A\),則需要再\(O(n)\)地處理\(A\)確定的半平面內的合法點對數目。可以按照極角序枚舉\(A\),並用單點指針確定半平面的范圍,即可在總計\(O(n^2)\)的時間復雜度內解決這部分問題。
再考慮計算\(BC\)的對數。發現\(AD\)對\(BC\)的限制很強,直接限定了\(BC\)的斜率(或言之方向向量,因為有可能斜率不存在)以及中點坐標,因此可以考慮直接\(O(n^2)\)預處理出所有\(BC\)點對,每次查詢時只需要在對應的值上二分找出范圍限制即可。總時間復雜度\(O(n^2\log n)\)。
具體實現上,根據“與向量\((a,b)\)點積相同的點在一條垂直向量\((a,b)\)的直線上,與向量\((a,b)\)叉積相同的點在一條平行向量\((a,b)\)的直線上”,我們可以直接通過點積與叉積確定\(BC\)的具體位置。
Day1T2 JOJO
考慮解決沒有\(2\)操作的子問題。
定義一個二元組\((x,c)\)表示有連續的\(x\)個\(c\)字符。考慮對一個由二元組組成的字符串求\(next\)數組。可以發現若\(next\)需要跨越某個二元組,則必須滿足前后綴對應二元組相等(起始位置除外),這里的二元組相等定義為每一維均相等。起始位置二元組的字符要求相等而長度不要求相等,但必須大於等於前綴的對應位置。
現在考慮加入一個二元組\((x,c)\)后如何快速求得答案。沿\(next\)鏈往前跳時,每次遇到字符為\(c\)的二元組時,都能夠為新加入的\((x,c)\)中的某一段區間找到其\(next\),其貢獻為一個公差為\(1\)的等差數列。特別的,新加入的\((x,c)\)的一段后綴可能只能與首位二元組匹配,此時的貢獻是首位二元組長度的若干倍。
這樣就解決了沒有\(2\)操作的子問題。當出現\(2\)操作時,常規的求解\(next\)數組的做法就不再適用了——求解\(next\)數組的時間復雜度是均攤的。因此,我們需要對求\(next\)的過程進行優化。首先離線建樹,在自上而下\(dfs\)的過程中,維護每條\(next\)鏈上的\(kmp\)自動機(即\(f_{i,j,k}\)表示在\(i\)狀態下加入一個二元組\((j,k)\)后\(next\)會指向哪里,同時即\(g_{i,j,k}\)表示對答案產生的貢獻),每次修改是復制前一位的\(f\)數組,並將\(f_{i,x,c}\)修改為\(i\),將\(g_{i,x,1...c}\)修改為一個首項為前\(i-1\)個二元組總長度\(+1\),公差為\(1\)的等差數列,查詢即查詢前綴和。用可持久化線段樹實現即可,復雜度\(O(n\log n)\)。
具體實現上,可以對\(g_{i,j,k}\)全體減下標,這樣修改就變成了區間賦值。最后再加上下標的等差數列即可。
Day1T3 多邊形
最優策略下每次旋轉操作一定滿足\(d=n\),因此總步數就等於初始局面下兩端點均不是\(n\)的邊的條數(邊界上的邊不計入)。
將初始局面下所有與\(n\)相鄰的點(包括\(1\)和\(n-1\))排序,這樣游戲終止的條件就變成了將。對於排序后相鄰的兩點\(l,r\),必然存在一條邊\((l,r)\),否則原圖就不是一個合法的三角剖分。此時若\(l+1=r\)那么這條邊沒有操作空間,可以直接忽視,否則在\((l,r)\)中必然存在一個點\(p\)使得\((l,p),(p,r)\)均有邊,這樣對\((l,r)\)進行一次旋轉操作后即可得到邊\((p,n)\),同時相等於在\((l,r)\)中間插入了一個數\(p\)。接下來區間\((l,p)\)與\((p,r)\)獨立,方案數即可分別計算。
不難發現上述過程中隱含的樹形結構。以初始局面下所有\(l+1<r\)的邊\((l,r)\)作為樹根,即可得到一片二叉樹森林,其中每個節點必須在其父親操作之后才能操作。這樣方案數邊不難計算:合並兩棵子樹的方案數為組合數。
對於一次任意的旋轉操作,可以發現要么是在最優策略下操作了一步使得最優步數減\(1\),在樹形結構上的表現為刪除某棵二叉樹的根,將其左右子樹分別作為兩棵新的二叉樹,要么是對某個點(而且一定是左兒子)進行了一次\(rotate\)操作,不改變最優步數。兩種情況的方案數均可以通過計算組合數逆元求出。
時間復雜度\(O(n+m)\)或\(O((n+m)\log n)\)。
Day2T1 校園旅行
有一個\(O(m^2)\)的暴力\(dp\)做法,記\(f_{i,j}\)表示是否存在一條從\(i\)到\(j\)的回文路徑,轉移即分別枚舉\(i,j\)的相鄰點轉移即可。
這個\(dp\)可以進行一些小優化,額外記\(g_{i,j}\)表示\(i\)是否存在一個相鄰點\(i'\)滿足\(f_{i',j}=1\),這樣轉移就只需要枚舉一遍,復雜度\(O(nm)\)。
(不知道怎么想的)考慮減少邊數。對於所有連接同色點的邊,每一個連通塊內只需要保留一棵生成樹即可得到與原圖相同的結果。特別的,若該連通塊不是二分圖,則需要添加一條自環。對於所有連接異色點的邊做同樣的事情,這樣總邊數就降到了\(O(n)\)級別,再套用上面的做法即可。
Day2T2 白兔之舞
考慮枚舉實際走了\(i\)步,會發現走\(i\)步的方案數恰好就是\(\binom Li\)。於是我們要求的答案就是:
隨手單位根反演一下
令\(a_j=((\omega_k^jA+I)^L)_{x,y}\),然后原式(差不多)就變成了
這不就是裸的\(FFT\)?
對於\(k=2^n\)的Case,直接\(FFT\)即可,復雜度\(O(3^3k\log L+k\log k)\),期望得分\(60\)分。
出考場后:
接着lun給我丟了份16年myy的集訓隊論文。
所以接下來我們做剩下的\(40\)分。
考慮\(jt=\frac{(j+t)^2-j^2-t^2}{2}\),於是原式就變成了
移項后就可以直接卷積了!
然而此時你會發現\(\frac{x^2}{2}\)這東西有可能不是整數,也即需要用到\(2k\)次單位根,而和(du)善(liu)的lun一定也會造數據來卡你。
不過不要慌,轉念一想,你會發現還有一種轉化的方法:
\(jt=\binom{j+t}{2}-\binom j2-\binom t2\)
於是愉快地套用上式就行啦。
\(MTT\)實現卷積即可,復雜度依舊為\(O(3^3k\log L+k\log k)\)。
Day2T3 序列
證明?看官方題解吧
第一個結論是答案的構造方式:從左到右依次加數,當發現當前的數比前邊的數字小的時候就將整段替換成所有數的加權平均值,用一個單調棧即可維護。這同時也是\(50\)分\(O(nm)\)的做法。
以下所有下標(除了\(x\))均為單調棧的下標而非原序列的下標。
現在對\(x\)位置進行了權值的修改。考慮最終答案里\(x\)位置的所在區間(段),設之為\([L_0,R_0]\),則\([1...L_0-1],[R_0+1...n']\)在最終答案中的結構與單獨考慮之的結構是一樣的。也就是說只需要維護出所有前后綴的單調棧,再想辦法對於每次詢問找出\(L_0,R_0\)就行了。
考慮枚舉一個\(R\)檢查其是否可以作為\(R_0\),對這個\(R\)找出以其為右端點向左合並時會合並到的位置,記其為\(L\),則若\([L,R]\)的平均數小於\(R+1\)的權值則這個\(R\)就是我們要找的\(R_0\)。
枚舉\(R\)可以改為用二分實現,同時對於一個給定的\(R\)我們可以通過在線段樹上二分的方式做到\(O(\log n)\)地找到其對應的\(L\)。因此總復雜度\(O(n\log n+m\log^2n)\)。