2024.11 做題記錄

Fractured_Angel發表於2024-11-15

一個月封閉集訓,可能題會稍微多一點。

001. P8940 [DTOI 2023] C. 不見故人 提高+/省選-

先來分析一下題目:寫的還是比較簡潔的,就是把區間裡的數都變為它們的 \(\gcd\)。然後分析一下所謂代價,發現如果 \(k\) 小的時候會比較麻煩些,而 \(k\) 大起來反倒好辦,就是直接全域性改一下就好。

\(Key\space Observation\space 0\) 首先如果所有數都相等,那麼答案一定為 \(0\)

\(Key\space Observation\space 1\) 所有數最終都會變成全域性的 \(\gcd\)。這就讓我們可以對題目進行一步等價轉化——將所有數除掉全域性 \(\gcd\),最終所有的數就都可以變成 \(1\)

\(Key\space Observation\space 2\) 每個數最多會被包含在一次修改中。這也是顯然的,考慮反證:如果一個數被包含在兩次修改中,那麼為什麼我們不將兩次修改合併呢?這樣改兩次代價只會更大,與題目要求的最小性矛盾,故證畢。這就啟發我們進行階段性動態規劃。

考慮動態規劃,定義 \(f_i\) 代表前 \(i\) 個數字最少經過多少次修改才能全部變為 \(1\)。轉移較為顯然,即若 \(\gcd(a_{j+1},...,a_i)=1\),那麼 \(f_i=\min(f_i,f_j+i-j+k)\),因為這個過程是倒著來的,所以我們也倒著列舉 \(j\) 方便快速遞推 \(\gcd\)

如若 \(n \leq 10^5\),那麼可以考慮二分和 ST 表最佳化掉找左界這一步,但 \(n \leq 4\times 10^6\),導致我們我們必須想其他辦法來最佳化這個動態規劃。但這步啟發我們可以把貢獻拆為 \(f_j-j\)\(i+k\) 兩部分方便最佳化。

\(Key\space Observation\space 3\) 如若把 \(a_i=1\)\(i\) 看做分界點,把序列分為 \(m\) 塊,則在塊的最右端點進行轉移才是有意義的。這啟發我們找到塊,按照塊進行動態規劃。 Old Friend's Trick

找到塊是簡單的但細節較多,怎樣處理看個人喜好。定義 \(g_i\) 為到第 \(i\) 塊最少要經過多少次修改才能全部變為 \(1\)。則有兩種轉移方式,第一種是塊內轉移,\(g_i=g_{i-1}+r_i-l_i+1+k+[\gcd(a_{l_i},...,a_{r_i}) \neq 1]\),其中 \([p]\) 為艾弗森括號,\(p\) 是一個命題,若其為真,則 \([p]\)\(1\),否則為 \(0\)。第二種是隔塊轉移,這樣就保證在轉移區間內。一定存在 \(j\) 使得 \(a_j=1\)。那麼 \(g_i=\min\{g_j+r_i-l_{j+1}+k+1\}\),向上面一樣拆貢獻維護最小值即可,甚至不用單調佇列。注意第二種轉移的 \(j\) 要保證 \(j<i-1\),處理時有些細節。答案即為 \(g_m\)

002. CF1292B Aroma's Search 1700

題目選自:2020, 2021 年 CF 簡單題精選

題意就是它給你生成了一個點列 \((x_i,y_i)\)。這個點列是無窮的,給定了 \((x_0,y_0)\),生成方式是 \((x_i,y_i)=(a_x \times x_{i-1}+b_x,a_y \times y_{i-1} + b_y)\)。現在給定起點 \((x_s,y_s)\),詢問 \(t\) 時間內最多能經過多少個點。

\(Key\space Observation\space 1\) 注意到 \(a_x\)\(a_y\) 都是大於等於 \(2\) 的,所以在值域內的點其實是 \(\log V\) 量級的,這是一個非常非常關鍵的性質,它的出現讓我們可以隨便寫都能過。

我們直接暴力生成這個點列,然後直接暴力列舉起點。這都是 \(\log V\) 帶給我們的自信。

\(Key\space Observation\space 2\) \(x_i\)\(y_i\) 是單調上升的,所以一個一個走的曼哈頓距離等價於直接走過去的曼哈頓距離。發現就好,證明是顯然的。

然後在列舉起點的同時,直接暴力列舉左右端點,因為注意到,我們最多最多就只能折返一次,然後列舉是先向右走還是先向左走即可。時間複雜度 \(O(\log^3 V)\),做完了。

*003. CF1313D Happy New Year 2500

題目選自:2020, 2021 年 CF 簡單題精選

題意很簡潔,不需要我過多敘述。

\(Key\space Observation\space 0\) 資料範圍 \(1\leq k \leq 8\)。看到這麼小的資料範圍我們應該在能力範圍內可以想到以下演算法:暴搜,狀壓,容斥。當然我們目前並沒有任何頭緒。

\(Key\space Observation\space 1\) 差分,我們注意到區間加在差分意義下就是單點加減,又注意到資料範圍,\(n\) 在可接受範圍內而 \(m\) 則不可接受,印證了我們這一猜想。

\(Key\space Observation\space 2\) 一維掃描線,值域廣而有用的點不多應讓我們想到了 【模板】掃描線。我們嘗試借鑑掃描線的思路進行某種操作。Happy New Year's Trick

我們先把有用的 \(2\times n\) 個點加入一個陣列裡面。按照位置順序排序,同時終點在前,起點在後,這是為了方便以後處理。並方便進行掃描線操作。並且儲存時我們不妨給每個操作點一個權,起點為 \(i\),終點為 \(-i\),這方便我們下面來對接,也方便終點在前的排序。

接下來進入主體部分。利用 \(k\) 的性質,我們進行狀態壓縮動態規劃。

我們定義第 \(i\)\(i+1\) 這段區間為第 \(i\) 個操作段。\(i\)\(1\)\(2\times n\) 之間。特殊的,如果 \(i\) 的位置和 \(2\times n\) 一樣,那麼這個操作段的長度就為 \(0 \),表示整體操作結束。

\(Key\space Observation\space 3\) 我們定義 \(f_{i,j}\) 為第 \(i\) 到第 \(i+1\) 個操作段的 起點選擇狀態為 \(j\)\(j\) 是一個二進位制串,因為保證了 \(1 \leq k \leq 8\),所以 \(j\) 最多有 \(8\) 位。但因為我們不知道你這些操作具體是什麼,所以我們開一個 \(vis\) 陣列記錄這些對你這個區間有影響的操作編號。 Happy New Year's Trick 2

接下來考慮怎麼轉移,我們先記第 \(i\) 個操作段的長度為 \(len_i\)

討論第 \(i\) 個操作點是起點還是終點:

  1. 若第 \(i\) 個操作點為起點,那麼我們考慮怎麼刻畫這個事情。因為我們不考慮你起始操作點的順序,因為你已經存了編號,所以你隨便找一個沒有被佔用的位置佔用上即可,即把 \(vis_j\)\(0\) 的一個點改為自己的 \(id\),並用這個點的位置去進行更新。想到這個後轉移就較為容易,列舉二進位制串 \(0\)\(2^k\)。如果串 \(p\) 中存在 \(j\) 這一位,那麼就是直接逆推,考慮前面沒加上這一位時的答案,即 \(f_{i,p}=f_{i-1,p \oplus 2^j}+len_i \times (popcount(p) \bmod 2)\),如果沒有 \(j\) 這一位,那麼也是簡單的,就是 \(f_{i,p}=f_{i-1,p}+len_i \times (popcount(p) \bmod 2)\)
  2. 若第 \(i\) 個操作點為終點,其實與起點是類似的。因為這是終點,所以前面必會有一個點對其有影響且是它對應的起點。我們找到這個點後,它就對這段區間沒有了影響,把它取消標記,也就是將其 \(vis\) 值設定為 \(0\)。同時記錄位置 \(j\)。那麼轉移也是非常簡單的,如果串 \(p\) 中已經存在 \(j\) 這一位,那麼就是不合法的,因為我們已經將第 \(j\) 位去除了,賦值為無窮大。否則 \(f_{i,p}= \max\{f_{i-1,p \oplus 2^j},f_{i-1,p}\}+len_i \times (popcount(p) \bmod 2)\)

最後一步是統計答案,就是 \(f_{2\times n,0}\),就是最後一個操作段已經不能被任何操作點所影響。注意一下初始值是全部 \(f\) 陣列賦值為無窮大,而 \(f_{0,0}\) 單點為 \(0\)。所以這題我們就做完了。

004. CF1322B Present 2100

題目選自:2020, 2021 年 CF 簡單題精選

題意簡潔,不過多敘述。

非常妙的一道題。看到 \(a_i \leq 10^7\),我們可以考慮到二進位制題目的一些經典操作。

\(Key\space Observation\space 1\) 按位統計答案,這看不出來的話你就沒救了。

\(Key\space Observation\space 2\)\(k\) 位是不是 \(1\) 只與前 \(k\) 位有關,與其他的無關,所以可以將每個 \(a_i \bmod 2^k\) 放到 \(b_i\) 裡進行下一步操作。Present's Trick

\(Key\space Observation\space 3\)\(k\) 位是否是 \(1\) 要分兩種情況討論,第一種是兩數之和不進位到上面,那麼兩數之和應該在 \([2^k,2^{k+1})\),第二種是進位到上面,那麼兩數之和應該在 \([2^{k+1}+2^k,2^{k+2}-2]\) 內。

有了第 \(3\) 個關鍵發現,我們就可以排序雙指標解決這個問題,那麼第 \(k\) 位對其的貢獻就是 \((cnt \bmod 2) \times 2^k\),最後都加起來即可。於是我們就做完了。

005. P5094 [USACO04OPEN] MooFest G 加強版 普及+/提高

題目摘自:我的《分治全家桶》部落格

非常魔怔的一道題,反正分治就很人類智慧的呢。這題感覺樹狀陣列做法是綠,分治做法得有藍,因為不太好想。題目問的是 \(\sum \sum \max(v_i,v_j)\times |x_i-x_j|\)

看到絕對值我們考慮先把絕對值拆掉再說。按照鍵值 \(x_i\) 從小到大排序。這樣式子變成 \(\sum \sum \max(v_i,v_j)\times (x_j-x_i)\)。為什麼要分治呢?因為前面那個非常討厭的 \(\max(v_i,v_j)\)

我們繼續考慮後序遍歷 CDQ 分治(或者說序列分治)。在遞迴計算完左右兩個子區間的答案後,左右區間內部天然的 \(x\) 鍵值順序就顯得沒那麼重要了。我們考慮打亂它,因為在整體合併中,我們只需要知道 右區間的所有 \(x\) 值都比左區間的 \(x\) 值大 就夠了。這一點與第一題的歸併排序有異曲同工之妙。

考慮我們合併要幹什麼東西。我們要處理的是 左右兩邊的 \(v\) 鍵值 對除自己外另一半區間的影響。這啟示我們按照 \(v\) 進行排序。

合併時,每次加一個左區間的 \(v\) 鍵值對右半區間的貢獻顯然是:

\[v_i \times ((\sum^{j-1}_{k=mid+1}x_k)-x_i \times (j-mid-1)) \]

每次加一個右區間的 \(v\) 對左區間的貢獻顯然是:

\[v_j \times (x_j \times (i-l)-(\sum^{i-1}_{k=l}x_k)) \]

注意邊界情況也要加上這兩種貢獻哦!不然你就會輸出 \(0\)。也有更簡潔的寫法,不過自認為我的這個比較清晰易懂。

006. P3403 跳樓機 提高+/省選-

同餘最短路模板題,但是真的好高妙啊,誰能想到要這麼做啊?

我們發現如果直接連邊 SPFA 或者直接連邊 Dijkstra 都會直接炸的死死的。我們只能另闢蹊徑。

\(Key\space Observation\space 1\) 我們發現雖然 \(h\) 大的離譜,但是 \(x,y,z\) 的值域都在可接受範圍內。啟發我們對這三個值下手。

\(Key\space Observation\space 2\) 我們不妨對 \(x\) 進行考慮,如果一個數 \(k\) 可行,那麼 \(k+px \leq h\) 的這一系列數都是很可行的。所以我們不妨找到一些很小的數,然後讓他們加上這些 \(px\) 得到最終的個數。

\(Key\space Observation\space 3\) 考慮如何不重不漏的計數,我們尋找不變數,在加 \(x\) 的過程中什麼不變呢?對了,就是模 \(x\) 的餘數不變!於是我們考慮按照餘數分類。按照上面說的,我們對模 \(x\) 的每一個餘數找一個最小可行的數,把它不斷加上 \(x\),計算不超過 \(h\) 的個數然後加起來即可。

\(Key\space Observation\space 4\) 如何實現這一點呢?我們考慮把模 \(x\) 完全剩餘系中的 \(x\) 個數看做 \(x\) 個點,又因為我們還有 \(+y,+z\) 兩種操作,所以我們連 同餘邊。將 \(i\) 連到 \((i+y) \bmod x\),邊權為 \(y\)\(+z\) 是同理的。這樣我們跑一段最短路就可以找到模 \(x\) 的每一個餘數的那個最小可行的數。Drop Tower's Trick

然後這道題我們就做完了。

007. P2365 任務安排弱化版 提高+/省選-

一眼能看出來是動態規劃型別的題目。考慮樸素的動態規劃演算法。

這應該是簡單的。定義 \(g_{i,j}\) 代表前 \(i\) 個工作,分了 \(j\) 組做的最小費用。轉移應該是 \(g_{i,j}=g_{l-1,j-1}+(j \times s + sumt_i) \times (sumf_i-sumf_{l-1})\),只要你沒讀錯題就能想到這個 \(O(n^3)\) 的動態規劃。

\(Key\space Observation\space 1\) 本題最為神奇也是用到的唯一一個大 Trick —— 費用前置。在我們的推導中,我們發現複雜度的瓶頸在於分割的段數,但這又很難最佳化掉,因為我們需要知道花費了幾倍的 \(s\)。但很遺憾,用普通的方法我們並不能解決或是規避這個問題。但是我們考慮一個 \(s\) 的影響,它能影響什麼東西。很顯然,這段時間可以影響在它後面的所有東西,又因為乘法是存在分配律的,所以我們就相當於把 \(s\) 這個費用前置,提前把它對後面的影響加上了,這樣也就不必要知道具體有幾個 \(s\) 了。費用前置 Trick/ Task Arranging Trick

008. [ABC379F] Buildings 2 1659 藍牌題

感覺真的不好做啊?可能是我對這兩種技巧都太生疏了吧。

你發現如果你單純想預處理出所謂的“最左邊的能看到你的”,那說明你跟我一樣讀錯題了。因為題目中定義的“看見”這一動作是不完全具有單調性的。但這還是啟示我們使用單調棧演算法。

我們反過來考慮“你能看見啥”。那麼我們考慮倒過來使用單調棧。

\(Key\space Observation\space 1\) 我們考慮信友隊 NOIP 2024 聯訓 2024.11.13 的 T4 做法。在處理這樣的多組詢問問題時,我們考慮將詢問離線。其實那題的做法也和這題差不太多。

我們考慮處理一組 \((l,r)\) 詢問。考慮到我們從後往前使用單調棧,所以我們的單調棧實際上是一個單調遞減棧。考慮樓啥時候能被看見。首先它的位置得大於 \(r\),其次它一定得比 \([l,r]\) 內的最大值要大,再次在 \([l,x]\) 中應該不存在比它高的建築。

\(Key\space Observation\space 2\) 考慮在單調棧上二分。二分一個第一個位置比 \(r\) 大,那麼從它一直到棧底的數都是可以的,那麼這組詢問的答案就是這段距離。

我們分別處理 \(m\) 組詢問,這樣我們這道題就做完了。

*009. [ABC379G] Count Grid 3-coloring 2304 黃牌題

打星是因為這題用到了一個狀態壓縮動態規劃的經典最佳化 Trick——輪廓線狀態壓縮動態規劃。

\(Key\space Observation\space 1\) 這其實是一個非常重要的發現,就是你發現如果 \(n\times m \leq 200\),那麼 \(min(n,m) \leq \sqrt{n\times m}\) 約等於 \(14\)。這就直接把做法指向了狀態壓縮動態規劃。但考慮到樸素的按行轉移狀態壓縮動態規劃的時間顯然是承受不住的,於是我們把做法指向 ——> 輪廓線狀態壓縮動態規劃

我們欽定 \(m<n\),並定義 \(f_{i,j,mask}\) 為填完前 \(i\) 行,在 \(i+1\) 行填完 \(j\) 個,左邊 \(j-1\) 個和上一行的右邊 \(j\)\(m\) 組成的一個三進位制狀態 \(mask\) 的合法組成個數。(這顯然是一個三進位制狀壓動態規劃,實際上其狀態約只有 \(2^m\) 個,但也沒有必要進行壓縮因為時間上和空間上都可以透過)

首先我們先預處理出 \(f_{1,0,mask}\) 這是可以直接 \(O(m\times 3^m)\) 暴力列舉解決的。

考慮怎樣轉移,因為對於每一個格子,能影響它的只有上邊和左邊兩個格子,先把這兩個格子的狀態按照某種方式快速求出來。然後列舉這一格的狀態 \(0/1/2\)。然後你再以某種方式快速預處理出 \(newmask\),直接暴力轉移即可。注意碰到邊界要轉移到下一行。

最後答案即為 \(\sum f_{n,0,mask}\)。注意狀態要使用 unordered_map 儲存,以達到卡常,節約時間,節約空間的目的。

010. [ABC377E] Permute K times 2 1685 藍牌題

一道與置換有關的趣題。不是很難,但很有趣就是了。

首先考慮我們的熟知結論 Exercise G's Trick,如果輪換陣列為一個排列,那麼所有數都必然會在自己的置換環上行走。

考慮顯然的先把所有的環搜出來,記錄一下每個數字在哪個環上,環長,環上位置實際對應的數字,這都是簡單的,可以一併記錄。

\(Key\space Observation\space 1\) 倍增置換。考慮第一次置換後 \(i\) 位置的情況,記為 \(b_i\),則 \(b_i\) 顯然等於 \(a_{a_i}\)。考慮第二次置換,設 \(i\) 位置上的數被置換到了 \(c_i\),則 \(c_i=b_{b_i}=a_{a_{a_{a_i}}}\)。於是置換 \(k\) 次走 \(2^k\) 步。 Permute K times' Trick

考慮到 \(k\) 非常大,於是我們可以使用快速冪,後面的問題就簡單了。於是這題我們就做完了。

011. [ABC377G] Edit to Match 1782 藍牌題

這場 G 是不是有點簡單,這個字典樹提示的其實有點明顯了。首先資料範圍給的是 \(|S|\),說明與總長度有關,聯想到 Trie 的節點數也與總長度有關。所以每次插入一個串到字典樹中。

然後肯定是字首能取就取,後面的話就取一個最短的字尾。最短字尾就記一個陣列,最後隨便統計一下就做完了,這題是真水題了。

*012. P5999 [CEOI2016] kangaroo 省選/NOI-

連續段動態規劃 / 插入型別動態規劃 / kangaroo's Trick

非常強的一個動態規劃技巧。常見的型別如有一個波浪形的限制,就像這道題一樣。還有排列計數和不能重複用一些數這種限制。還有 \(n\) 最好在 \(n^3\)\(n^2\) 範圍內,這是比較可以接受的。

首先我們不考慮起點 \(s\) 和終點 \(t\) 的限制,只考慮求任意起點和任意終點的排列。

定義 \(f_{i,j}\) 為用了前 \(i\) 個數,連續段個數為 \(j\) 個的排列方案數。我們一般情況下的轉移為:

  1. 增加一個連續段:\(f_{i,j}=f_{i,j}+f_{i-1,j-1} \times j\)。因為原先有 \(j-1\) 個連續段,所以有 \(j\) 個空格可以插入。
  2. 合併兩個連續段:\(f_{i,j}=f_{i,j}+f_{i-1,j+1}\)。原先有 \(j+1\) 個連續段,有 \(j\) 個空格可以供插入合併。

需要注意的是,因為陣列天然有序,所以我不管怎麼合併都是滿足條件的。

下面考慮 \(s\)\(t\) 的限制。考慮如果 \(i=s\)\(i=t\)。那麼它們有兩種轉移。一個是插入到第一個/最後一個連續段的開頭/末尾,一個是另起第一個連續段/最後一個連續段。就是 \(f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\)

同時前面的另起連續段部分也要變一變。因為在插入 \(s\)\(t\) 之後我們就已經欽定了所謂的“第一個”和“最後一個”連續段。所以在 \(i\) 足夠大的時候我們就不能往序列兩邊插入連續段了,這樣會破壞起點和終點的性質。最後轉移變為 \(f_{i,j}=f_{i,j}+f_{i-1,j-1} \times (j-[i>s]-[i>t])\),其中 \([p]\) 為艾弗森括號,\(p\) 為一個命題,若 \(p\) 為真,則 \([p]\)\(1\),否則 \([p]\)\(0\)

然後這道題我們就做完了。

*013. P7967 [COCI2021-2022#2] Magneti 省選/NOI-

有了上一題的鋪墊,我們這道題就是相對簡單的。考慮 kangaroo's Trick,進行連續段動態規劃。

考慮先按照 \(r_i\) 排序,方便轉移。記 \(f_{i,j,k}\) 表示考慮了前 \(i\) 個磁鐵,連續段個數為 \(j\),佔用空間為 \(k\) 的方案個數。這題我們有三種轉移(有一種是在這題可行,而在 kangaroo 那題是不合法的)

  1. 增加一個連續段:沒啥好說的,就是 \(f_{i,j,k}=f_{i,j,k}+f_{i-1,j-1,k-1} \times j\)
  2. 合併兩個連續段:就是 \(f_{i,j,k}=f_{i,j,k}+f_{i-1,j+1,k-2\times r_i + 1} \times j\),這也是簡單的。
  3. 插入到已有連續段的兩端:其實也不難,就是 \(f_{i,j,k}=f_{i,j,k}+f_{i-1,j,k-r_i} \times j \times 2\)\(\times 2\) 的原因是連續段的兩邊都可以插入。

最後答案就是 \(\sum f_{n,1,i} \times \binom{l-i+n}{n}\),後邊那個組合係數是插板法得出的。

於是這道題我們就做完了。

014. P8865 [NOIP2022] 種花 普及+/提高

補了補 NOIP2022,發現第一題還是比較水的,就非常常規。

首先考慮 'C' 字型怎麼做,非常簡單,預處理出右邊最長 \(0\) 串個數,記為 \(r_{i,j}\)。然後你考慮怎麼構成這個 'C',其實就是下面隨便選一行,隨意選一個可行長度,然後你自己這一行隨意選一個可行長度。你考慮由乘法分配律對後面可行的狀態做一個字尾和,記為 \(sumc_{i,j}\),答案就是 \(\sum sumc_{i+2,j} \times r_{i,j}\)

然後考慮 'F' 字型怎麼做,稍微難一點點,考慮預處理出下面最長 \(0\) 的個數,記為 \(d_{i,j}\)。然後你考慮 'F' 和 'C' 有啥關係。無非就是 'C' 下面拼了一行,'F' 下面拼了一個 'L' 型的東西,這是一樣的,記一下 \(sumf_{i,j} = sumf_{i+1,j}+d_{i,j} \times r_{i,j}\),答案就是 \(\sum sumf_{i+2,j} \times r_{i,j}\)

然後我們就做完了,過於簡單了。

*015. P3047 [USACO12FEB] Nearby Cows G 普及+/提高

歷史遺留問題了屬於是。大概是兩年前剩的題了。現在依然不會,沒有長進,惱!

考慮 \(f_{i,j}\) 為樹上第 \(i\) 個點,距離為 \(j\) 的點權和。考慮到其實 \(f_{i,j}\) 是很不好轉移的。所以我們設立另一個狀態 \(g_{i,j}\),代表樹上第 \(i\) 個點的子樹內,與 \(i\) 距離為 \(j\) 的點的點權和。

首先我們可以一遍 DFS 求出 \(g_{i,j}\) 這是簡單的。就是 \(g_{u,0} = a_u\)\(g_{u,i} = \sum g_{v,i-1}\)

然後考慮怎樣求出 \(f_{i,j}\),考慮子樹內的貢獻,就是 \(g_{i,j}\)。子樹外的貢獻我們使用容斥原理,就是 \(f_{u,i-1}-g_{v,i-2}\) ,我覺得這個式子非常困難,最好畫圖理解一下。也有可能是我容斥學的過於的不好了。

小最佳化:注意到我們可以重複利用 \(f_{i,j}\) 這個陣列。但為了避免重複/缺少貢獻,我們可以在換根的時候倒著列舉 \(k\) 來解決這一個問題。但其實不用這個最佳化也沒什麼事。