計數類 DP

xishanmeigao發表於2024-10-25

P9522 [JOISC2022] 錯誤拼寫

牛魔計數題使我旋轉。

主要說一下分析思路:

根據字典序的比較方式我們可以轉化一下 \(T_{A_j}<T_{B_j}\) 這個條件。我們現在只考慮嚴格小於的情況。

字典序暗示我們要從後往前 DP,於是設 \(f_{i,j}\) 表示 \(s_i=j\) 的方案數。然後考慮從 \(f_{i',j'}\) 轉移過來,思考轉移的約束。

這個約束就是對於一段 \([A,B]\) 裡面的 \(i'\),對 \(i<A\) 都要少掉一段貢獻,這段貢獻和 \(j\) 有關。

於是設 \(h_j\) 表示 \(i+1\sim n\)\(f_{*,j}\) 的貢獻。每次移動 \(i\) 的時候更新 \(h_j\)。用連結串列維護,時間複雜度 \(\mathcal{O}(n|\sum|)\)

計數題好難。

P2051 [AHOI2009] 中國象棋

即每一行、每一列最多有兩個棋子。

在轉移的時候,我們需要依賴於上一層的狀態,但往往我們無需清楚每個地方的狀態具體是啥,只需知道某種狀態的個數,然後使用排列組合計算。

\(f_{i,j,k}\) 表示前 \(i\) 行,有 \(j\) 列一個炮,有 \(k\) 列兩個炮。

列舉這一行放幾個炮直接轉移就行。

P3158 [CQOI2011] 放棋子

發現維護個數比較困難。

發現每種顏色都是相互獨立的,區域的劃分依據是完整的行和列。所以可以考慮設 \(f_{k,i,j}\) 表示前 \(k\) 種顏色佔據了 \(i\)\(j\) 列的方案(這類題通常都可這樣設),轉移需要一個 \(g_{i,j,k}\) 表示用 \(k\) 枚同色棋子佔據 \(i\)\(j\) 列的方案。

\(g\) 需要容斥。

P10982 Connected Graph

正難則反,我們求 \(n\) 個點的無向不聯通圖個數。

一開始為了不計算重複,我設了 \(f_{i,j}\) 表示 \(i\) 個點,形成 \(j\) 個聯通塊的方案,然後再它做容斥,但總有點問題。

一種可行的方法是:欽定一個點作為劃分依據。

比如以 \(1\) 號點為依據,設 \(f_i\) 表示 \(i\) 個點的聯通圖方案,轉移時考慮 \(1\) 號點所在聯通塊大小,讓其餘的點不與該聯通塊連邊即可。

CF1989E Distance to Different

觀察到 \(b\) 取決於 \(a\) 中相同陣列成的連續段。

\(f_{i,j}\) 表示前 \(i\) 個數分成了 \(j\) 個連續段的大小,轉移不表,注意要特殊處理 \(f_{i-2,j-1}\)

我想到這後還一直在想怎麼保證每種數都填進去了這 \(j\) 個段,後來才發現只需保證分成了至少 \(k\) 個段就行了,具體怎麼填根本不重要,因為對應的 \(b\) 都是一樣的。

所以第二維只用開到 \(k+1\),字首和最佳化。

自己實在太唐了。

CF1585F Non-equal Neighbours

感覺要記錄 \(i\) 選的數和 \(a_i\) 的大小關係。然後可能可以轉移??貌似沒人這樣做。

考慮容斥,欽定有 \(k\)\(i\) 滿足 \(b_i=b_{i+1}\),則 \(ans=\sum\limits_{k=0}^{n-1}(-1)^kF(k)\)

於是有一個樸素的 DP,設 \(f_{i,j}\) 表示前 \(i\) 個數,分成了 \(j\) 段的方案數,時間複雜度 \(\mathcal{O}(n^3)\)

然後發現我們需要的是段數的奇偶性,於是將狀態改為 \(f_{i,0/1}\),時間複雜度 \(\mathcal{O}(n^2)\)

發現轉移式裡不好最佳化的是一個 \(\min\),這個可以搞一個單調棧,然後加上一堆判斷搞定。

CF1909F Small Permutation Problem

很厲害的題。

先考慮 Easy Version。一開始編了一個 DP 做法,但好像不太對。

第一步轉化就是將 \((i,p_i)\) 放到座標系上,將問題轉化成放車問題,合法的放置要求每一行每一列都只有一個車。那麼 \(a_i-a_{i-1}\) 就對應著一個 L 字形裡的點對數量。實時維護 \((1,1)\sim(i,i)\) 還未放置的行列數量即可。

考慮 Hard Version,我們可以將一段 \(-1\) 縮起來,設區間 \((i,j)\) 之間全是 \(-1\),那麼相當於在一個 \(y\times y\) 的網格挖掉左下角 \(x\times x\) 的網格後形成的 L 字形網格中放 \(t\) 個車,其中 \(x=j-a_j\)\(y=i-a_j\)\(t=a_i-a_j\)。這個東西可以容斥。時間複雜度 \(\mathcal{O}(n)\)

[ARC180C] Subsequence and Prefix Sum

發現 \(0\) 有很大的影響。發現第一個選的數也有很大影響(但這種可以歸類為前面那種,因為初始值為 \(0\))。

一旦字首和出現了 \(0\),後面如果只選一個數,那麼跟不選沒區別。如果選不止一個數,那麼第一個數要是相同的選哪個都一樣。怎麼辦?

一開始想用最小表示法,但不會 。

發現自己智力不夠,思維混亂,我們只需再記錄 \(g_i\) 表示前面和為 \(i\) 且上一次為 \(0\) 的方案數。然後我們不用 \(f_{i-1,0}\) 轉移到 \(f_{i,a_i}\),這樣就避免了選一個數的情況。然後用 \(g_j\to f_{i,j+a_i}\),就計算上了不止選一個數的情況。每次迴圈後,\(g_{a_i}\leftarrow g_0\)\(g_0\leftarrow f_{i,0}\)

反思:思維混亂,在計數題中是大忌。要學會歸納各種情況,減少複雜的分類討論,規約為簡單的問題。

P9131 [USACO23FEB] Problem Setting P

先考慮狀壓出每道題的狀態,狀態相同的單獨考慮。然後思考轉化判定條件:其實就是滿足前一道題是後一題的子集。

那我們設 \(f_s\) 表示最後一道是 \(s\) 狀態的題,列舉子集直接轉移,時間複雜度 \(\mathcal{O}(3^m)\)

一個 Trick:將狀壓後的數折半,分成前後兩部分。

\(s_{i,j}\) 表示滿足 \(x\)\(10\) 位是 \(i\),後 \(10\) 位是 \(j\) 子集的 \(f_x\) 的總和。那麼轉移的時候只需列舉前 \(10\) 位,修改 \(s\) 時只需列舉後 \(10\) 位。可以做到 \(\mathcal{O}(n2^{m/2})\)

有一個大神 DP:考慮最佳化列舉子集,考慮每次加入一個元素,\(|S|\) 轉移,從集合 \(i\to i\cup \{s_1,s_2,\dots,s_k\}\)\(k!\) 種方式。

\(f_{i,j}\) 表示現在走到集合 \(i\),用了 \(j\) 步的方案。

  • 下一步走到的集合不選:\(f_{i\cup \{x\},j+1}\leftarrow f_{i,j}\)
  • 下一步走到的集合選:\(f_{i\cup\{x\},0}\leftarrow f_{i,j}\times \dfrac{1}{(j+1)!}\times val_{i\cup \{x\}}\)

反思:轉化判定條件,配合一些 Trick 的使用。

相關文章