连续段DP
对于一类与排列数量计数有关,或转移中需要与相邻插入元素有关的 DP。
通常情况下,对于排列计数问题,我们要确定一个插入元素的顺序,并将插入的元素按如下方式操作:
- 构成新的一段
- 加入某一段的开头或结尾
- 连接两段
Phoenix and Computers
设
根据上述方式转移:
- 新建段:
dp_{i+1,j+1}\leftarrow dp_{i,j}\times (j + 1) - 加入某一段:
dp_{i+1,j}\leftarrow dp_{i,j}\times 2 \times j,dp_{i+2,j}\leftarrow dp_{i,j}\times 2\times j - 合并块:
dp_{i+2,j-1} \leftarrow dp_{i,j}\times2\times(j-1),dp_{i+3,j-1} \leftarrow dp_{i,j}\times (j-1) 注意合并块不能有dp_{i+1,j-1}\leftarrow dp_{i,j}\times(j-1), 因为如果两端相隔 1,中间那个会自动打开,这样会在 3.1 的转移中会算重(感谢 @Zhang_xiangyou 更正)。
Magneti
发现方案数可以根据磁铁的排列方式计算出来,不同的磁铁排列方式影响了磁铁段的最短长度。
先按照
设
-
新建段:
dp_{i+1,j+1,k+1}\leftarrow dp_{i,j,k}\times(j+1) -
加入某一段:
dp_{i+1,j,k+r_{i+1}}\leftarrow dp_{i,j,k}\times 2\times j -
合并块:
dp_{i+1,j-1,k+r_{i+1}+r_{i+1}-1}\leftarrow dp_{i,j,k}\times (j-1)
[JOI Open 2016] 摩天大楼
给你一个数列
把
显然,把直线往下平移一个单位后,增加的 f(纵坐标之差)为
转移大概长这样:
int t = a[i] - a[i + 1];
(dp[i + 1][j + 1][k + 2 * (j) * t][0][0] += dp[i][j][k][0][0] * (j + 1)) %= mod;//建新段
(dp[i + 1][j + 1][k + (2 * (j) - 1) * t][1][0] += dp[i][j][k][1][0] * j) %= mod;//建新段
(dp[i + 1][j + 1][k + (2 * (j) - 1) * t][0][1] += dp[i][j][k][0][1] * j) %= mod;//建新段
(dp[i + 1][j + 1][k + (2 * (j) - 2) * t][1][1] += dp[i][j][k][1][1] * (j - 1)) %= mod;//建新段
(dp[i + 1][j][k + 2 * j * t][0][0] += dp[i][j][k][0][0] * 2 * j) %= mod;//加入某段
(dp[i + 1][j][k + (2 * j - 1) * t][1][0] += dp[i][j][k][1][0] * (2 * j - 1)) %= mod;//加入某段
(dp[i + 1][j][k + (2 * j - 1) * t][0][1] += dp[i][j][k][0][1] * (2 * j - 1)) %= mod;//加入某段
(dp[i + 1][j][k + (2 * j - 2) * t][1][1] += dp[i][j][k][1][1] * (2 * j - 2)) %= mod;//加入某段
(dp[i + 1][j - 1][k + 2 * (j) * t][0][0] += dp[i][j][k][0][0] * (j - 1)) %= mod;//连接两段
(dp[i + 1][j - 1][k + (2 * (j) - 1) * t][0][1] += dp[i][j][k][0][1] * (j - 1)) %= mod;//连接两段
(dp[i + 1][j - 1][k + (2 * (j) - 1) * t][1][0] += dp[i][j][k][1][0] * (j - 1)) %= mod;//连接两段
(dp[i + 1][j - 1][k + (2 * (j) - 2) * t][1][1] += dp[i][j][k][1][1] * (j - 1)) %= mod;//连接两段
// 建新段 (开头)
(dp[i + 1][j + 1][k + (2 * (j) - 0) * t][1][0] += dp[i][j][k][0][0]) %= mod;
(dp[i + 1][j + 1][k + (2 * (j) - 1) * t][1][1] += dp[i][j][k][0][1]) %= mod;
// 建新段 (结尾)
(dp[i + 1][j + 1][k + (2 * (j) - 0) * t][0][1] += dp[i][j][k][0][0]) %= mod;
(dp[i + 1][j + 1][k + (2 * (j) - 1) * t][1][1] += dp[i][j][k][1][0]) %= mod;
// 加入某段并成为开头
(dp[i + 1][j][k + (2 * j - 0) * t][1][0] += dp[i][j][k][0][0]) %= mod;
(dp[i + 1][j][k + (2 * j - 1) * t][1][1] += dp[i][j][k][0][1]) %= mod;
// 加入某段并成为结尾
(dp[i + 1][j][k + (2 * j - 0) * t][0][1] += dp[i][j][k][0][0]) %= mod;
(dp[i + 1][j][k + (2 * j - 1) * t][1][1] += dp[i][j][k][1][0]) %= mod;
kangaroo
和上一道题思路,状态类似,但是要简单很多。