1. 程式人生 > 實用技巧 >5 月做題記錄

5 月做題記錄

懶得寫題解了,就在這裡放吧。。
不建議你們當成題解看,因為不是認真寫的題解。

有的題會去補詳細題解。

CF704D


先全染貴的那個顏色,然後看最多能染幾個便宜的顏色。
建二分圖,左邊行,右邊列,加個上下界限制,最大流就是最多能染幾個便宜的顏色。

P3558


簡單 DP
首先第一個數不會變,所以所有數下界 -1
其次弄出來大於 1 的數一定不優,如果弄出來個大於 1 的數,就往後看,發現 -1 還是要加兩次,0 還是要加一次,這樣一定不比把這個數搞成 1 優。
於是
\(dp_{i,j}\) 代表第 \(i\) 個數在第 \(j\) 個段,列舉這個和上一個直接轉移。

P3554


先二分答案

首先每次肯定要染完所有兒子,然後剩餘的次數相當於可以攢著以後用。
自上而下沒法做,想到自下而上,反過來統計至少要從上面借來多少次數。

樹形 DP,\(dp_u\) 代表 B 在 \(u\),這時 \(u\) 的整棵子樹至少需要再染幾個點(\(u\) 已染色) A 才能贏。
B 是可以往任意一個地方走的,轉移的時候要求和,不是取最大值。

CF1137C


\(d\) 很小,不同周每一天的情況又都是一樣的,考慮分層圖。
結合了分層圖和強連通分量,還是第一次見到。

P2851


完全揹包 + 多重揹包
找零上界一定不超過 \(2\times \max v_i^2\)

AGC020E


我是先想的如果沒有 1 變 0 操作應該怎麼做,明顯是個區間 DP。
\(f_{i,j}\)

代表 \([i,j]\) 方案數,\(g_{i,j}\) 代表縮成一個括號(以及只有一個字元的情況,01\(f\) 轉移的時候列舉最後一個括號位置。\(g\) 轉移就列舉區間長度 \(len\) 的約數(\(len\) 除外)把這些縮成一個括號。

這題 \(f\) 還是一樣的轉移方式,不過算 \(g\) 的時候不同了,需要轉移自的 \(f\) 需要是所有擷取部分 AND 起來的值,可能會產生新的字串,所以 DP 狀態裡就記字串而不是區間了,記憶化搜尋即可。

這個東西看起來複雜度很大但其實是對的,首先 \(f\) 刷出來的 \(f\) 不用考慮,因為 \(f\) 刷出來的 \(f\) 刷出來的 \(f\)

一定都是原來 \(f\) 的字串,還只有 \(n\) 個。而 \(f\) 也會產生 \(g\),並且 \(f\) 的每一個子串都會產生一個 \(g\) 的計算,而 \(g\) 又會產生新的 \(f\),這些產生出來的 \(f\) 是必須全部重新算的。

但是每次產生出來的 \(f\) 長度至多是原來 \(g\) 的一半,三次 \(g\) 產生 \(f\) 操作以後,長度就必定 \(\leq 13\) 了。長度為 \(n\) 的 01串只有 \(2^n\) 個,當 \(n\leq 13\) 的時候這其實並不大。

所以我們只需要考慮 \(g\)\(f\) 一次和兩次的情況就行了,而且分成 \(f\) 的次數兩次乘起來還得小於 8,不然就長度太小了。其實最後生成的串可以看作是最早的原串取了幾個字串拼起來的,這裡用兩次操作長度都減半舉例好了。

只用 \(i,j,k\) 就可以表示一個狀態,那麼狀態數不會超過 \(n^3\)
兩次分別為分兩段和分三段其實是一樣的,狀態數也只有 \(n^3\) 級別種。
實際值是遠小於理論值的。

一共有 \(n^3+2^{\frac{n}{8}}\) 個狀態,轉移用時 \(n^2\),時間複雜度 \(O(n^5+n^2 2^{\frac{n}{8}})\)

AGC020D

ARC101E

ARC101F


出口情況固定的機器人先扔掉,剩下的要麼在左邊的出口出要麼在右邊。
算出來到左右的距離,分別為 \(x\)\(y\),扔到座標系上,找一條只往上或往右的折線,折線及其上方為從左邊出,下方為右邊出,\(dp_i\) 表示折線最後一個點經過的是 \(i\) 的總不同方案數,人為加上 \((0,0)\)\((inf,inf)\)

P3174


用求直徑類似的方法跑一遍 DP 就行了,好像很簡單。

P5385


對於森林,連通塊數 = 點數 - 邊數。
維護關於邊加入時間的最大生成樹(森林),求出來對於每一條邊 \(e\) 最大的 \(l\) 使得只加入 \(l\) 以後 \(e\) 以前的邊可以使得 \(e\) 連線兩個連通塊。加入這條邊的如果生成森林裡斷了邊,斷掉的邊就是 \(l\),沒斷邊就認為 \(l\)\(0\)

後面的拿個主席樹搞了就好了。

P4298


有向無環圖最長反鏈板子。
第三問就強制選上這個點(刪去與這個點有偏序關係的所有點)再求一遍最長反鏈,看看會不會掛掉。

CF590E


二合一了
前半部分直接跑 AC 自動機就 T 了,要只找到最近的有串的節點,然後一路路徑壓縮過去。
後面就是 DAG 最長反鏈

CF516D


這個 \(f_x\) 很奇怪,應該就是這題的切入點。
那個式子就是個尺取的形式吧,資料範圍明示每次查詢可以 \(O(n)\) 回答(不過實際上需要 dsu)。  

然後就是要加點刪點維護最大連通塊,但是這個 \(f_x\) 性質沒用到,一般看到這種東西應該能想到和直徑有關,這題把直徑的重點當根,\(f_x\) 就是小根堆,證明也很顯然,就因為到一個點最遠的點必然是直徑的兩端之一。

這樣加點用並查集合並,而刪點可以直接刪,當前連通塊大小 -1 即可。因為不會把一個連通塊分成兩個了。

CF603E


首先那個點度數的條件一看就是要轉化,保證在儲存現有的邊的情況下,每一個連通塊大小都是偶數就可以了,如果不滿足可以刪掉一些邊,一定存在滿足條件的方案。

用 LCT 維護最小生成森林,然後列舉現有的在森林中的最大邊,能斷則斷,不能斷更小的斷掉也沒意義。這個東西用堆維護就好了。

最後一步直接判最大值能不能刪居然一直沒想到,大概是遇到這種問題還沒有這種意識。

AGC006B


寫道水題放鬆一下。
首先 \(1\)\(2n-1\) 不行,在倒數第二層一定就沒了。

注意到三個數中位數的一個性質,如果某兩數相等為 \(x\),則中位數一定為這個數 \(x\)
那我們儘量在倒數第二層構造出來這樣連續兩個,發現這樣一定能做到。

    x
   -xx
  --xx-
 ---xx--
---lrxl-- 
// l 為任意小於 x 的數,r 為任意大於 x 的數。
// 如果 x=2,這四位就改成 rlxr 就行了。

AGC006D


直接確定頂部,感覺和 easy 有相似的思路。
觀察那個兩格寬的填滿 x 的柱子,它一定是直接頂天的。
然後如果邊上還沒成柱子它還會向邊上擴充套件一格,成柱子了就不行。

------0------
-----001-----
----|0011----
---||0011|---
--0|001011|--
-01001010111-
   ^^    ^^
 0柱子  1柱子

但這樣還是沒法做,因為柱子太難形成了,沒法快速判斷。
這裡繼承上面的思路,二分答案,這樣所有數都改成 lxr 了。
這樣看起來會有問題,我們把二分答案的條件改成答案是否 \(\leq mid\) 就好了,這樣所有數都只剩下 lr,我們只要看 l 的柱子和 r 的柱子哪個裡中間近就可以了。

沒有柱子的要特判。

AGC010B


先把總和不是一次減的倍數的判掉。

這個東西一看就沒法直接做,一開始我考慮的是哪幾個位置分別作為幾次 5 的位置被減。
這個 1 2 3 4 5 (1) 差分完就變成 _1_1_1_1_-4(_) 了,感覺很可做,算出來一共減掉了幾次,就能求上面說的那個東西了,負數和非整數判掉即可。

AGC021D


正串和反串會同時修改是比較麻煩的地方,但是這也讓我們想到了一個特性。

  • 給定 \(i,j\),正串的前 \(i\) 個和反串的前 \(j\) 個的 LCS,等於正串的後 \(j\) 個和反串的後 \(i\) 個的 LCS。

\(dp_{i,j,k}\) 表示正串的前 \(i\) 個和反串的前 \(j\) 個的 LCS。

答案就是 \(\max_{i=0}^n{2\times dp_{i,n-i,k}}\)
但這樣並不對,顯然答案有可能為奇數所以不對

前半部分不一定匹配到最後了,中間那部分(短串最遠的匹配處到藍圈的位置處,綠色區域)如果不是空,一定至少能產生 1 的貢獻。但是如果不是像圖中藍色圈出的字元一樣,是 \(i\)\(n-i+1\) 的配對產生貢獻,這個貢獻一定可以在 \(i\) 為其他位置時被算進去,只有藍圈這樣的配對是不行的,因為 \(i\) 向右移動同時 \(n-i+1\) 向左移動,它們不會同時出現在分割線的一邊。解決方法也很簡單,答案加入這個就行了(只刪掉一個一定比刪掉不止一個優)
\(\max_{i=0}^{n-1}{2\times dp_{i,n-i-1,k}}+1\)

P4197


想辦法把那兩個條件解決掉
從點 \(v\) 開始只經過困難值小於等於 \(x\) 的路徑 \(\to\) Kruscal 重構樹
所能到達的山峰中第 \(k\) 高的山峰 \(\to\) 主席樹
對 dfs 序建主席樹就行了,最後能訪問的點一定是一整個子樹,在dfs 序上是連續的。

CF685C

CF568C


一眼 2-SAT,字典序最小就貪心,然後全是細節。

根據貪心從左到右逐位考慮,雖然看起來後面對前面會有限制,但是通過 2-SAT 轉成前面對後面的限制了。如果前面每一位都是頂上界的(和給定字串相等)首選就是對應字串的那一位,否則就是 'a'。

但這樣很可能是不合法的,也就是說後面不管怎麼填也沒法合法,不過這個並不難判斷,有以下兩種情況,假設目前判斷第 \(i\) 位能不能填給定字元。

  • \(i\) 位決定後,後面出現既必須為 'V' 又必須為 'C'
  • \(i\) 位決定後,前面仍然頂上界,假設第 \(i+1\)\(i+k\) 位選 'C' 還是選 'V' 已經確定,而 \(i+k+1\) 位沒確定或者這位已經是字串以外的位置,並且這一段無論怎麼選也選不出字典序比給定字串那段大的字串(比如 'V' 最大選 'b',\(i+1\) 位只能選 'V',而給定字串要求 'c')

第二種有點複雜

然後看最優情況下不行的話,還有什麼選項可以填,會決定一個位置對後面是否有解的影響的,無非就兩個。

  • \(i\) 位是否頂上界
  • \(i\) 位是 'C' 還是 'V'

四種情況列舉一下,即可,如果前面不頂上界了後面就不可能再頂了,注意字典序從小到大,我這裡實現的比較複雜。

P5155


挺妙的一道題。
從有一些點出發,按最優策略走的話,如果跳下,期望得分比繼續走大,那麼就一定選擇跳下。
其他點出發,一定是走到它左邊的第一個上一類點或者它右邊的第一個上一類點,然後跳下,左邊還是右邊的概率可以算出來。
選擇跳下的點構成一個凸包。
\(0\)\(n+1\) 是強制跳下,也把它們也加入凸包。