计数DP

重要思想

1.对于一个方案,贪心地求出可否若其满足更......的条件,那是否可以满足原条件;然后把贪心需要记录的信息记进状态里面,就可以得到一个多项式复杂度做法。
2.先得到一个多项式做法,再考虑优化这个做法,常见的有:

  • 优化状态,比如某几维之间记录的信息有关联,可以减少状态或者把状态转化为其他本质相同的信息
  • 优化转移,比如用前缀和优化

技巧与题型

按顺序DP

核心技巧:预分配位置、容斥

含背包要素

核心技巧:“集体涨水”

计数题中遇到多重背包:按模k分组,前缀和优化即可

排列计数

连续段DP

处理排列计数的利器。这类排列计数中对排列的要求多与排列中数的上升、下降有关,因而考虑先将原序列排序,然后进行计数。具体地,设 \(f_{i,j}\) 表示考虑了前 \(i\) 个数(排序后),目前有 \(j\) 个连续段的方案数。转移一般有三种状态:

  • 新开一段,即 \(f_{i,j}=f_{i,j}+j \times\ f_{i-1,j-1}\)

  • 把已有的一段延长,即 \(f_{i,j}=f_{i,j}+2 \times\ j \times\ f_{i-1,j}\)(头尾皆可延长,所以要乘2)

  • 合并两个已有的段,即 \(f_{i,j}=f_{i,j}+f_{i-1,j+1}\)

注意段与段之间通常是无序的,因而段的不同排列方式对应着不同方案。对于段之间顺序的统计在新开一段的过程中完成,要注意这一点。

例题

[ABC431F] Almost Sorted 2

考虑将原序列的数从小到大插入序列中,于是在插入 \(i\) 时,所有小于 \(i\) 的数都已插入,而所有值为 \(i\) 的数等价,因而可以记 \(cnt_i\) 表示值为 \(i\) 的数的个数。所有值为 \(i\) 的数只能插在值在 \([i-d,i]\) 范围的数之后,因而对于 \(i\) ,方案数为 \(C_{sum_i-sum_{i-d-1}}^{cnt_i}\),把所有的 \(i\) 的方案乘起来即可。代码

AT_dp_t Permutation

连续段DP模板题(但这道题似乎并不用把连续段表示出来)。考虑到一个排列,某个位置填某数的方案只与该数在序列中的相对排名有关,于是定义 \(f_{i,j}\) 表示考虑了前 \(i\) 个位置,\(i\) 位置上的数排名为 \(j\) 的方案数,转移如下:

  • \(f_{i,j}=\sum_{k=1}^{j-1} f_{i-1,k}\\\\\ [s_{i-1} == <]\)
  • \(f_{i,j}=\sum_{k=j}^{i-1} f_{i-1,k}\\\\\ [s_{i-1} == >]\)

转移可以前缀和优化。最后答案即为 \(\sum_{i=1}^{n} f_{n,i}\)代码

矩阵加速计数

对于一类线性递推计数问题,可能遇到 \(n\) 很大的情况,这个时候 \(O(n)\) 的 DP 都是不可通过的。这个时候就需要考虑写出转移矩阵,用矩阵乘法加速计数。这是一类重点问题,且可以出的有一定难度。

例题

P3702 [SDOI2017] 序列计数

考虑用总方案数减去一个质数都不选的方案,两个方案数都很好用矩阵转移。代码

众数计数

关于一个区间的绝对众数的相关计数问题,朴素的状态设计为 \(f_{i,j,k}\) ,表示要选为众数的数选了 \(j\) 个,非众数的数选了 \(k\) 个的方案数。但是考虑对绝对众数的要求是出现次数 > \(\lfloor \frac{n}{2} \rfloor\) ,因此我们只关心 \(j-k\) 的值,于是可以用 \(f_{i,j}\) 表示原状态中 \(j-k\) 的值,从而把 DP 从 \(O(n^3)\) 优化到了 \(O(n^2)\)

例题

P5664 [CSP-S 2019] Emiya 家今天的饭

这一道题就是上述优化的一个模板应用。代码

杂题

P3214 [HNOI2011] 卡农

好题。首先无序序列可以转化为有序序列,最后除以 \(m!\) 即可。考虑一个合法方案需要满足的条件:在 \(m\) 个片段中,

  • 任意片段不能选空集
  • 任意两个片段选出的集合不能完全相同
  • 任意一个元素出现的次数为偶数次

直接求满足条件的方案数显然不可行,考虑容斥。设 \(f_i\) 表示选到第 \(i\) 个片段,且当前片段序列满足上述三个条件的方案数。如果 \(1\)\(i-1\) 都已经确定,那么第 \(i\) 个片段唯一确定,所以总方案数显然是\(A_{2^n-1}^{i-1}\) 。根据条件一,在 \(i-1\) 位置已经满足条件的方案应当被减去(因为若在 \(i-1\) 满足条件,那么第 \(i\) 个片段只能选空集);根据条件三,若两个片段完全相同,那么删去这两个片段之后的方案依然合法,与第 \(i\) 个位置相同的位置可以有 \(i-1\) 种,一共有 \(2^n-i+1\) 种可能的集合,因此还要减去 \(f_{i-2}\times(i-1)\times(2^n-i+1)\)

即最终DP式子为:\(f_i=A_{2^n-1}^{i-1}-f_{i-1}-f_{i-2}\times(i-1)\times(2^n-i+1)\)代码

P10741 [SEERC 2020] Fence Job

一场联考的T2。注意到最终序列一定是以一段连续段涂成一个颜色的形式涂了若干段,且每个数能涂到的范围是一个区间。定义 \(f_{i,j}\) 表示用前 \(i\) 种颜色涂到 \(j\) 的方案数,转移有以下两种:

  • \(j\)\(i\) 能涂到的范围中,那么 \(f_{i,j}=f_{i-1,j}+f_{i,j-1}\)
  • \(j\) 不在 \(i\) 能涂到的范围内,那么 \(f_{i,j}=f_{i-1,j}\)
    注意每一轮从 \(j=0\) 开始转移。代码
posted @ 2025-11-05 22:36  lyc1119  阅读(9)  评论(0)    收藏  举报