SMOI-R1 賽後若干個月的總結

Supor__Shoop發表於2024-09-28

打得非常好的一場比賽,所以才來寫總結。

T1 「SMOI-R1」Queue

打表找規律題,太簽到了,不講。

T2 「SMOI-R1」Company

首先,如果要使得 \(x,y\) 的距離最後是儘可能遠的,我們就要考慮一些滿足最優解的性質。

不難想到一個結論:如果將初始時每一棵樹縮成一個節點,那麼最優解形成的新的樹必然是一條鏈,且鏈的兩端分別是初始的第 \(1,2\) 棵樹。因為只有這樣才能保證每一棵樹都對答案產生了影響,否則說明必然存在一棵樹是對 \(x,y\) 的距離毫無影響的。下文記 \(t_i\) 表示初始第 \(i\) 棵樹。

由於初始每棵樹都有根,且題中的連邊方式是讓葉子節點與樹根連邊,也就是說最終形成的樹也一定是一個有根樹。我們不妨來探討一下這個樹根:

  • 如果樹根為 \(t_1\) 或者 \(t_2\),這就很顯而易見了。假設現在樹根為 \(t_1\),那麼就說明樹中最深的節點就是 \(t_2\)。我們記錄一個當前節點下標 \(j\),初始時 \(j=x\),每次我們找到 \(j\) 所在的樹 \(t_i\) 中距離 \(j\) 最遠的一個葉子節點 \(j\prime\),接著讓 \(j\) 移動到 \(j\prime\) 處,這個時候我們再考慮跳到另一個樹的根上。總的來說,我們到達的樹的先後順序是 \(1\to (3\to 4\to\dots\to n)\to 2\),括號中的可以任意改變順序。因此,對於所有 \(i>2\)\(t_i\)\(j\) 在其中移動的距離一定是樹的深度;對於 \(t_1\)\(j\) 移動的距離是 \(x\) 到某一個葉子節點的最遠距離;對於 \(t_2\)\(j\) 移動的距離則是 \(y\) 的深度。計算此類情況的答案時,只需要計算它們的和就行了。

  • 如果樹根不是 \(t_1\) 或者 \(t_2\),則說明 \(j\) 的移動方向是先往上,後往下。我們考慮列舉這個樹根 \(t_k\)\(k>2\))。由於 \(j\) 在除了 \(t_1,t_2,t_k\) 以外的所有樹中經過的距離都是其深度,所以這些樹的排放並不重要。而對於 \(t_1,t_2\)\(j\) 的其中移動的距離分別是 \(x,y\) 在樹中的深度;對於 \(t_k\),由於它肯定是選擇了兩個葉子節點進行連邊,所以 \(j\) 在其中移動的最大距離就是最遠的兩個葉子節點之間的距離。由於列舉的時間複雜度是 \(O(n)\),我們記 \(d_i\) 表示第 \(i\) 棵樹的最大深度,然後計算 \(sum=\sum_{i=3}^n d_i\),每次計算除了 \(t_1,t_2,t_k\) 之外的樹的總代價時,我們只需要用 \(sum\) 減去 \(d_k\) 就行了。

綜上,對於 \(t_1,t_2\),我們要計算 \(x,y\) 在其中的深度以及 \(x,y\) 到達某個葉子節點的最長距離。對於 \(t_i\)\(i>2\)),我們要計算 \(d_i\) 以及其中最遠的兩個葉子節點的距離。然後按照上述方法計算就行了。

但是要注意,樹與樹之間的連邊也會計入答案。考慮到最終的樹的組成形態,我們可以知道一共會新增 \(n-1\) 條邊,並且它們都會計入 \(x\to y\) 的最長距離。

T3 「SMOI-R1」Game

首先有一個非常常規的思路:列舉每一個數,求出其作為最大值的區間個數。這道題也是如此。

如果最終的序列 \(a\) 長度夠小,那麼我們可以利用單調棧得出每一個數 \(a_i\) 向前和向後的第一個 \(>a_i\) 的數 \(a_{l_i}\)\(a_{r_i}\),如果找不到則分別為 \(0,|a|+1\),那麼 \(a_i\) 作為最大值的區間就有 \((i-l_i)(r_i-i)\) 個。

但是需要注意,有的區間是會被算重的,比如 \(a=\{1,2,1,2\}\),兩個 \(2\) 分別計算得到的區間都包含 \([2,4],[1,4]\)。這個時候考慮更改 \(l_i\) 的定義為向前找到的第一個 \(≥a_i\) 的數的下標,如果找不到則為 \(|a|+1\),計算方式仍然是 \((i-l_i)(r_i-i)\)。這樣就可以保證不會重複計算了,原因顯然,因此把它當成一個 Trick 掌握就行了。

但是出題人非常可愛啊!這個 \(a\) 的長度可以達到 \(10^{15}\),不可能直接計算。但是 \(n\) 的範圍倒是很合理。這就說明我們要找到 \(a\) 序列的計算規律。

我們將 \(a\) 視為 \(n\) 個形如 \(\{1,2,\dots,b_i-1,b_i\}\) 的子段前後拼接得到的序列,記 \(s_{i,j}\) 表示第 \(i\) 個子段中的第 \(j\) 個數在 \(a\) 中的下標。

先考慮 \(s_{i,b_i}\) 的代價該怎麼計算。由於各個子段的單調性,我們可以知道其向前能夠找到的第一個 \(≥b_i\) 的數的下標一定是 \(s_{j,b_j}\)\(j<i\)),向後能夠找到的第一個 \(> b_i\) 的數的下標一定是 \(s_{k,b_i+1}\)\(i<k\)注意是 \(b_i\) 而不是 \(b_k\))。可以發現 \(b_j\)\(b_i\) 向前第一個大於等於它的數,\(b_k\)\(b_i\) 向後第一個大於它的數。可以透過這個結論,利用單調棧找到 \(j,k\)。我們找到這兩個 \(j,k\) 記為 \(L(i),R(i)\)(如果不存在滿足條件的 \(j\)\(k\),則分別變為 \(0,n+1\)),則 \(s_{i,b_i}\) 的區間個數就是 \((s_{i,b_i}-s_{L(i),b_{L(i)}})(s_{R(i),b_{i}+1}-s_{i,b_i})\),可以利用 \(b_i\) 的字首和解決。

我們記 \(sum_i=\sum _{j=1}^ib_j\),那麼:

\[(s_{i,b_i}-s_{L(i),b_{L(i)}})(s_{R(i),b_{i}+1}-s_{i,b_i})=(sum_i-sum_{L(i)})(sum_{R(i)-1}-sum_i+1+[R(i)\leq n]) \]

每一個子段的頂點值我們就算完了,我們考慮將這種做法擴充到其它數上面。其實我們可以利用影像去思考:

我們知道對於每一個數,要去找前面和後面比自己大的,由於除了頂點以外,每個數的後面都有一個比自己大一的數,所以右側比較好找。而對於左側,我們結合上圖去思考,不難發現最終形成的 \(a\) 序列就是若干個等腰直角三角形排在一起,那麼每個數作為最大值的區間可以延展到的左端點一定是在 \(s_{k,b_k}\) 的位置。因此我們像之前那樣用一個單調棧就行了。

但是對於每個數都用單調棧的話顯然會寄。

我們可以找一下規律:

考慮對第 \(3\) 個子段的每個元素的答案進行統計,我們發現 \(IK\) 這一段對應過去的線段是 \(EF\),這說明 \(1\leq j\leq |IK|\) 的元素 \(s_{3,j}\) 可以向左延伸的最遠點都是 \(F\),即 \(s_{2,b_2}\)。而 \(GL\) 對應過去是 \(CD\) 的一個子線段,這說明 \(|IK|<j\leq |GH|\) 的元素 \(s_{3,j}\) 向左延伸的最遠點都是 \(s_{1,b_1}\)。這啟示我們將每一個子段中的元素分成若干個子部分進行處理,並且要保證每個部分的 \(s_{i,j}\) 對應的左側端點是相同的。

於是我們用單調棧去儲存 \(b_i\),維護一個單調遞減的子序列的下標。當我們要處理第 \(i\) 個子段時,我們就在棧中從後往前遍歷,設當前遍歷的元素為 \(p\),上一個遍歷的元素是 \(q\),那麼我們就將 \([s_{i,b_q+1},s_{i,b_p}]\) 的答案進行加和,下文令 \(t=sum_{i-1}-sum_p+b_q\)\(m=b_p-b_q\),則:

\[\begin{aligned} \sum _{j=b_q+1}^{b_p}j(s_{i,j}-s_{p,b_p})&=\sum _{j=b_q+1}^{b_p}j(sum_{i-1}-sum_p+j)\\ &=\sum _{j=1}^{m}(b_q+j)(sum_{i-1}-sum_p+b_q+j)\\ &=mb_qt+\dfrac{m(b_q+t)(m+1)}{2}+\dfrac{m(m+1)(2m+1)}{6} \end{aligned} \]

這樣就可以 \(O(1)\) 計算這一段的答案之和了。

根據單調棧的性質,可以推斷 \(n\) 個子段分成的子部分的個數和是 \(O(n)\) 級別的,可以跑過。

T4 「SMOI-R1」Apple

聽網友說是個 CF 裡面經典的 Trick,但是我不知道,不然我應該可以拿 \(10\) 塊錢了。。。

不過不要慌,只要我現在會了就行。

對於這種進位制上的子集問題,我們難以使用線段樹這一類樹形資料結構進行維護,所以我們可以考慮這樣一個 Trick:對於一個 \(2^n\) 不可過但是 \(2^{\lfloor \frac{n}{2}\rfloor}\) 可過的題,我們把所有進位制位分為前 \(\lfloor \frac{n}{2}\rfloor\) 位和後 \(n-\lfloor \frac{n}{2}\rfloor\) 位,然後對左半邊的 \(2^{\lfloor \frac{n}{2}\rfloor}\) 個狀態維護資訊。

由此,我們設 \(sum_i\) 表示 \(i\) 的子集中,所有右半邊與 \(i\) 相同的狀態 \(j\) 的權值之和。那麼對於每次的修改操作,如果是對狀態 \(i\) 進行修改,則我們找到所有滿足 \(i\subseteq j\)\(i,j\) 右半邊相同的狀態 \(j\),然後對 \(sum_j\) 進行修改,由於右半邊固定,我們只需要列舉左半邊,這樣每次的時間複雜度就是 \(O(2^{\lfloor \frac{n}{2}\rfloor})\)。對於每次的詢問操作,我們就找到所有滿足 \(j\subseteq i\)\(i,j\) 左半邊相同的狀態 \(j\),然後對所有的 \(sum_j\) 進行加和,由於左半邊固定,我們只需要列舉右半邊的子集,這樣每次的時間複雜度就是 \(O(2^{n-\lfloor \frac{n}{2}\rfloor})\)

因此總時間複雜度就是 \(O(q2^{\frac{n}{2}})\)

整體來說,感覺和 meet in the middle 的結構是差不多的,都是做到了把時間複雜度上的指數減少一半。

相關文章