dp 好题

DP题单

AGC002F Leftmost Ball

给你 \(n\) 种颜色的球,每个球有 \(k\) 个,把这 \(n\times k\) 个球排成一排,把每一种颜色的最左边出现的球涂成白色(初始球不包含白色),求有多少种不同的颜色序列,答案对 \(10^9+7\) 取模。

将这 \(n\times k\) 个球看成 \(n\) 个白球和 \(n\times k - n\) 个其他颜色的球,由于白球是每种颜色的第一个,所以要保证对于每个位置前缀白球个数均大于等于其他颜色种类数。

\(f_{i,j}\) 表示已经放了 \(i\) 个白球和 \(j\) 个其他球的方案数 \((i\ge j)\)

转移时考虑最后一个放的什么:

  • 白球:\(f_{i-1,j}\)

  • 新的颜色:\(f_{i,j-1}\times (n-j+1) \times \binom{n\times k-i-(j-1)\times(k-1)-1}{k-2}\)

    这里,我们将每个颜色的球的贡献全算上,在剩下 \(n-j+1\) 个颜色中任选一个放在 \(i\),然后该颜色还剩 \(k-2\) 个球,放在后面 \(n\times k-i-(j-1)\times(k-1)-1\) 个空中。


AGC001E BBQ Hard

\(n\) 个数对 \((a_i,b_i)\),求出

\[\sum_{i=1}^{n}\sum_{j=i + 1}^{n}{a_i+b_i+a_j+b_j \choose a_i+a_j} \]

答案对 \(10^9+7\) 取模

\(2\le n \le 200000,1\le a_i,b_i\le 2000\)

观察到 \(n\) 很大而值域很小,考虑对值域进行操作。

\(\binom{a_i+b_i+a_j+b_j}{a_i+a_j}\) 这个形式其实就是在坐标系上从 \((-a_i,-b_i)\) 只能向上或向右走,走到 \((a_j,b_j)\) 的方案数,所以我们就是要求任意两点间路径方案数的和。

考虑如何把复杂度降下来,如果能对每个点算其他所有点到它的路径方案数,就可以 \(O(n)\) 枚举了。

\(f_{i,j}\) 表示所有关键点到 \((i,j)\) 的方案数之和,转移也很简单 \(f_{i,j} = f_{i-1,j}+f_{i,j-1}\),初值 \(f_{-a_i,-b_i}=1\)

但题目要求 \(i<j\),所以减掉 \(\binom{a_i+b_i+a_i+b_i}{a_i+a_i}\) 后除以 \(2\) 即可。


ARC074C RGB Sequence

有一个序列 \(\left\{a_{i}\right\}\),要给序列中的每个元素一种颜色:红/绿/蓝。有 \(M\) 条限制 \((l,r,x)\),表示格子 \([l,r]\) 中颜色的种数要恰好为 \(x\),问可行的方案数,答案对 \(10^9+7\) 取模。

\(1\le n,m\le 3000\)

观察到只有三种颜色,所以可以记录下每个颜色最后出现的位置。

\(f_{i,j,k}\) 表示考虑到第 \(i\) 个数,另外两种颜色最后出现在 \(j\)\(k\),钦定 \(j>k\),转移很显然

  • \(f_{i+1,j,k}+=f_{i,j,k}\)
  • \(f_{i+1,i,k}+=f_{i,j,k}\)
  • \(f_{i+1,i,j}+=f_{i,j,k}\)

对于限制,挂到 \(r\) 上,在每次向后转移前更新一遍,由于已经知道了另外两个颜色最后的位置,就可以得到 \([l,r]\) 中的颜色数了,将不合法的改为 \(0\) 即可。


P2501 [HAOI2006]数字序列

给定一个长度为 \(n\) 的序列 a,求最少需要改变多少个数才能让 \(a\) 单调严格上升,同时每个数改变的绝对值之和最小是多少。

\(1\le n\le 3.5\times 10^4,1\le a_i\le 10^5\)

第一问

考虑保留最多的数,对于 \(i<j\),如果 \(a_i,a_j\) 可以保留,那么至少 \(a_j-a_i\ge j-i\),也就是 \(a_i-i\le a_j-j\)

\(b_i=a_i-i\),其最长不下降子序列即为答案。

第二问

在第一问的基础上,找到两个在 \(b\) 的最长不下降子序列上相邻的点 \(i,j\),那么 \(\forall k \in (i,j),b_k<a_i 或 b_k>b_j\),我们要把中间的这部分改成合法的,不难想到最优情况下一定是前一部分改成 \(b_i\),后一部分改成 \(b_j\),这个中间点直接枚举是可以过的(

同时 dp 记录一下 \(f_i\) 表示 \([1,i]\) 都合法的最小代价即可。


P3943 星空

有一串长为 \(n\) 的灯泡,其中有 \(k\) 个灯是关的。给定 \(m\) 个可以翻转的长度,也就是每次可以翻转这 \(m\) 个长度之一的灯泡,求最少操作多少次可以将所有灯泡打开。

\(n \le 40000, m\le 64,k\le 8\)

将灯的开关状态看为 \(0/1\),每次操作都是让一段区间异或 \(1\)。这种区间操作可以利用差分,这样每次操作修改两个位置。

由于最多有 \(8\) 个灯是关着的,所以差分数组中最多有 \(16\)\(1\),我们要把这些 \(1\) 都改成 \(0\),预处理出相邻两个之间的距离状压 dp 即可。

具体来说,先 bfs 求出想改变两个相距 \(x\) 的位置的状态最少需要操作几次,然后转移时枚举最后修改的两个位置。


P3830 [SHOI2012]随机树

Description

\(2\le n\le 100\)

第一问

比较简单,设 \(f_i\) 表示 \(i\) 个点的树的叶节点平均深度的期望,转移

\[f_i=\dfrac 1 x(f_{i-1}\times (i-1)+f_{i-1}+2)=f_{x-1}+\dfrac 2 x \]

第二问

\(f_{i,j}\) 表示有 \(i\) 个叶子,深度 \(\ge j\) 的概率,转移时枚举左儿子叶子数

\[f_{i,j}=\dfrac{1}{i-1}\sum_{k=1}^{i-1}f_{k,j-1}+f_{i-k,j-1}-f_{k,j-1}\times f_{i-k,j-1} \]

只要有一边深度 \(\ge j-1\) 就行了,减掉两边都 \(\ge j-1\) 的,至于左子树的大小 \(k\),取 \([1,i-1]\) 的概率都是相等的,所以直接除以 \(i-1\)


P3058 [USACO12NOV]Balanced Cow Breeds G/S

给一个只有左右括号的字符串,然后用 HG 两种字符来标记这个序列,所有标记 H 的括号可以组成一个正确的括号序列,所有 G 标记的括号也组成一个正确的括号序列,然后输出这种标记方案的总数 \(\bmod 2012\) 的值。

由于有两个标记,考虑分开记录,设 \(f_{i,j,k}\) 表示到第 \(i\) 个字符,打 H 标记的剩下 \(j\) 个 "(",打 G 标记的剩下 \(k\) 个 "(" 的方案数。

转移时判断是什么括号,然后分讨打 HG 标记

  • 如果是 "("
    • H: \(f_{i,j,k}+=f_{i-1,j-1,k}\)
    • G: \(f_{i,j,k}+=f_{i-1,j,k-1}\)
  • 如果是 ")"
    • H: \(f_{i,j,k}+=f_{i-1,j+1,k}\)
    • G: \(f_{i,j,k}+=f_{i-1,j,k+1}\)

最后答案为 \(f_{n,0,0}\),第一维可以滚动。


P4798 [CEOI2015 Day1]卡尔文球锦标赛

数位 dp

如果使用 dfs 写法,记忆化数组空间需要 \(O(n^2)\),记录当前在第几位,以及前面填的数中最大是多少,但很可惜空间开不下,放个代码

Code
int dfs(int cur, bool lim, int pre)
{
    if(cur == n + 1) return 1;
    if(!lim && ~f[cur][pre]) return f[cur][pre];
    int res = 0, up = lim ? a[cur] : pre + 1;
    for(int i = 1; i <= up; i++)
        res = add(res + dfs(cur + 1, lim & (i == up), max(pre, i)));
    if(!lim) f[cur][pre] = res;
    return res;
}

考虑递推,用时间换空间,设 \(f_{i,j,0/1}\) 表示前 \(i\) 个数,最大值为 \(j\),是否顶着上界的方案数。

转移时从 \(f_{i,j,0/1}\) 向后转移,讨论最后一个填什么

\[f_{i+1,j,0}+=f_{i,j,0}\times j+f_{i,j,1}\times (a_{i+1}-1) \\ f_{i+1,j+1,0}+=f_{i,j,0} \\ f_{i+1,j,1}+=f_{i,j,1}\ (a_{i+1}\neq j+1) \\ f_{i+1,j+1,1}+=f_{i,j,1}\ (a_{i+1}=j+1) \]

第一维可以滚动。


AT2087 Friction

给定一个 \(n\times m\) 的矩阵,矩阵上每个格子都有一个小写字母表示颜色,每次操作可以选择一列向下推一格,然后最下面的格子会消失,操作一次的代价为操作前这一列中与旁边格子颜色相同的对数。

求将整个矩阵全消掉所需要的最小代价。

\(1\le n \le 300,2\le m\le 300\)

首先有个性质,任意相邻的两列的代价相互独立,也就是相邻两列的贡献可以单独算。

证明:考虑消掉三列的代价,相邻两列间相对操作顺序是不受第三列的影响的,并且两列如果不相邻是不会产生贡献的,所以代价可以单独计算。

接下来就是简单 dp 了,设 \(f_{i,j}\) 表示第一列还剩 \(i\) 个格子,第二列还剩 \(j\) 个格子消完的代价。

\[f_{i,j}=min(f_{i-1,j},f_{i,j-1})+w(i,j) \]

其中 \(w(i,j)\) 表示这个状态下的代价,可以递推出来,假设当前在算第 \(x\) 列和第 \(y\) 列的贡献

\[w(i,j)=w(i-1,j-1)+[s_{i,x}==s_{j,y}] \]

于是就可以 \(O(n^2m)\) dp 了。


CF327E Axis Walking

给你一个长度为 \(n\ (1\le n\le 24)\) 的正整数序列 \(S\),再有 \(k\ (0\le k\le 2)\) 个正整数。

求有多少种 \(S\) 的排列方式使得其前缀和不会成为那 \(k\) 个数里的任意一个。 答案对 \(10^9+7\) 取模。

看到 \(n\le 24\) 不难想到状压,设 \(f(S)\) 表示当前已经确定的数为 \(S\),转移时枚举一个没有在 \(S\) 中的数 \(i\),并判断 \(sum(S)+a_i\) 是否合法。

这样复杂度是 \(O(2^nn)\),理论上 3s 是能过的,但是由于这是远古 CF 题,时限减半没能卡过去,考虑换一种转移方式。

对于一个状态 \(S\),我们可以枚举最后一个数是多少,然后从前面的状态转移过来,这个可以用 \(lowbit\) 优化,常数极小,可以通过。


P3451 [POI2007]ATR-Tourist Attractions

给出一张有 \(n\) 个点 \(m\) 条边的无向图,每条边有边权。

你需要找一条从 \(1\)\(n\) 的最短路径,并且这条路径在满足给出的 \(g\) 个限制的情况下可以在所有编号从 \(2\)\(k+1\) 的点上停留。

每个限制条件形如 \(r_i, s_i\),表示停留在 \(s_i\) 之前必须先在 \(r_i\) 停留过。

注意停留不等于经过

\(2\le n\le2\times10^4\)

\(1\le m\le2\times10^5\)

\(0\le k\le\min(20, n-2)\)

\(2\le r_i, s_i\le k+1, r_i\not=s_i\)

观察到 \(k\) 很小,依然可以状压,所以思路就很清晰了

先对这 \(k\) 个点跑一遍最短路,设 \(f_{S,i}\) 表示从 \(1\) 出发,已经在 \(S\) 中的点停留过,最后待在 \(i\) 的最短路,转移时枚举不在 \(S\) 中的 \(j\),要先判断能否在 \(j\) 停留,这个对每个点存一个需要的状态即可。

这样做空间复杂度是 \(O(2^nn)\) 的,大概是 80MB 左右,并不能过,需要卡空间。

其实差的并不多,只需要把 \(i\)\(S\) 中去掉,也就是当前在 \(i\) 节点,那么状态里就不要存是否有 \(i\),这样空间就是 \(O(2^{n-1}n)\) 了。


CF1067D Computer Game

\(n\) 个游戏,每个游戏有收益 \(a_i\),升级后的收益 \(b_i\),每次成功概率 \(p_i\)。每秒可以玩一个游戏,如果成功则得到当前收益,并且可以升级任意某个游戏。求 \(t\) 秒后的期望收益的最大值。

\(n\le 1e5,t\le 10^{10},a<b\)

不难发现,我们要想办法将 \(b_ip_i\) 最大的那个升级,然后后面一直玩这个游戏。

设计一个 dp,\(f_t\) 表示还剩 \(t\) 秒时期望收益最大值

\[\begin{align*} f_t&=\max\{p_i(a_i+(t-1)mx)+(1-p_i)f_{t-1}\} \\ &=\max\{p_i((t-1)mx-f_{t-1})+p_ia_i\}+f_{t-1} \end{align*} \]

其中 \(mx\)\(b_ip_i\) 的最大值,然后里面的可以斜率优化,将 \(p_i\) 看作 \(k\)\(p_ia_i\) 看作 \(b\),维护一个直线的凸包即可。

注意到 \((t-1)mx-f_{t-1}\) 是单调不降的,因为 \(mx\) 已经是最大的,\(f_t\) 的增量不可能大于 \(mx\)

所以可以直接扫一遍凸包上的直线,二分可行的转移区间,矩阵快速幂优化,这样做是 \(O(n\log^2n)\) 的。

因为已经确定了决策点,可以直接矩阵快速幂

\[\begin{bmatrix} f_t & t & 1 \end{bmatrix} \times \begin{bmatrix} 1-p_i & 0 & 0 \\ p_imx & 1 & 0 \\ p_ia_i & 1 & 1 \end{bmatrix} = \begin{bmatrix} f_{t+1} & t+1 & 1\end{bmatrix} \]

把二分改成矩阵倍增即可做到 \(O(n\log n)\)

维护凸包、倍增找可行转移区间时都是判断直线交点的横坐标。


T249441 【愚蠢的戴夫】豌豆射手

在长为 \(m\) 的草坪上放 \(n\) 个豌豆射手,每个豌豆射手有一个攻击半径 \(r_i\),求有多少种放置方案使豌豆射手不互相攻击。答案对 \(10^9+7\) 取模。

\(n\le 40,m\le10^5,1\le r_i\le 40,\sum r_i\le m\)

观察到 \(d\) 很大,而 \(\sum{r_i}\) 却只有 \(1600\),考虑先尽量让豌豆射手挨着放,这样后面会空出来一段,然后再把后面的空位分配到前面。

相邻的豌豆射手间的距离是 \(\max(r_i,r_j)\),所以我们按 \(r_i\) 排序,从小到大放,就可以知道每个豌豆射手的贡献了。

\(f_{i,j,k}\) 表示放了前 \(i\) 个豌豆射手,形成了 \(j\) 个连续段,共占了 \(k\) 个草坪的方案数。

转移时对 \(i+1\) 进行分类讨论

  • 单独形成一段,有 \(j+1\) 段空区间,多占了一个草坪

    \(f_{i+1,j+1,k+1}+=f_{i,j,k}\times (j+1)\)

  • 与前面某一段连起来,有 \(j\) 段,每段的两端都可以,连起来多占的长度一定为 \(r_{i+1}\)

    \(f_{i+1,j,k+r_{i+1}}+=f_{i,j,k}\times 2j\)

  • 连接了两段,有 \(j-1\) 个区间,多占的长度为 \(2r_{i+1}-1\),因为自己被算了两遍

    \(f_{i+1,j-1,k+2r_{i+1}-1}+=f_{i,j,k}\times (j-1)\)

然后考虑如何计算后面的空位分配给前面有多少种,其实就是插板法,前面共有 \(n\) 个豌豆射手,每个前面都可以加空位,设第 \(i\) 个豌豆射手前面加了 \(a_i\) 个,那么 \(\sum a_i=n-s\),其中 \(s\) 为已经占的草坪,方案数就是 \(\binom {n-s+n}{n}\)


P6944 [ICPC2018 WF]Gem Island

\(n\) 个人,最开始每个人手中有 \(1\) 颗绿宝石,每天晚上,会随机选一个绿宝石分裂为两个。

\(d\) 个晚上后绿宝石数量最多的 \(r\) 个人的绿宝石数和的期望值。

\(1\le n,d\le 500,1\le r\le n\)

设最后每个人的宝石的序列为 \(\{a_i\}\),可以证明每种情况是等概率的(可以用归纳法)。

那么我们要求的就是所有序列中前 \(r\) 大的和,考虑 dp

\(f_{i,j}\) 表示 \(a_1+a_2+\cdots+a_i=j\) 的所有序列中前 \(r\) 大的和,这里 \(a_i\) 为第 \(i\) 个人多了几个宝石,所有的和即为 \(f_{n,d}+r\)

\[f_{i,j}=\sum_{k=0}^{\min(i,j)}\binom i k\left( \right) \]

AGC013D Piling Up

一开始有 \(n\) 个颜色为黑白的球,但不知道黑白色分别有多少,\(m\) 次操作,每次先拿出一个球,再放入黑白球各一个,再拿出一个球,最后拿出的球按顺序排列会形成一个颜色序列,求颜色序列有多少种。答案对 \(10^9+7\) 取模。

\(n,m\le 3000\)

操作共有四种:

  • 取白,放黑白,取白
  • 取白,放黑白,取黑
  • 取黑,放黑白,取白
  • 取黑,放黑白,取黑

法向我们可以知道每次操作白球个数如何变化,令 \(f_{i,j}\) 表示操作 \(i\) 次,有 \(j\) 个白球的方案数。

但是最后是把拿出的球按顺序排列形成一个颜色序列,会出现白球个数不同但取球方案相同的,这样会算重,考虑如何去重。

我们将白球个数变化曲线放到一个平面上,对于平移后可以重合的都是相同的方案,所以我们只计算那些碰到过 \(x\) 轴的,就可以不重不漏了。

那么状态为 \(f_{i,j,0/1}\),最后的 \(0/1\) 表示是否碰到过 \(x\) 轴,转移也比较好写。

Code
for(int i = 0; i <= n; i++) f[0][i][!i] = 1;
for(int i = 0; i < m; i++)
    for(int j = 0; j <= n; j++)
    {
        if(j - 1 >= 0) // 第一次取白
        {
            // 第二次取白
            if(j == 1) f[i + 1][j - 1][1] = add(f[i + 1][j - 1][1] + f[i][j][0]);
            else f[i + 1][j - 1][0] = add(f[i + 1][j - 1][0] + f[i][j][0]);
            f[i + 1][j - 1][1] = add(f[i + 1][j - 1][1] + f[i][j][1]);
            // 第二次取黑
            if(j == 1) f[i + 1][j][1] = add(f[i + 1][j][1] + f[i][j][0]);
            else f[i + 1][j][0] = add(f[i + 1][j][0] + f[i][j][0]);
            f[i + 1][j][1] = add(f[i + 1][j][1] + f[i][j][1]);
        }
        if(j + 1 <= n) // 第一次取黑
        {
            // 第二次取白
            f[i + 1][j][0] = add(f[i + 1][j][0] + f[i][j][0]);
            f[i + 1][j][1] = add(f[i + 1][j][1] + f[i][j][1]);
            // 第二次取黑
            f[i + 1][j + 1][0] = add(f[i + 1][j + 1][0] + f[i][j][0]);
            f[i + 1][j + 1][1] = add(f[i + 1][j + 1][1] + f[i][j][1]);
        }
    }

P4456 [CQOI2018]交错序列

我们称一个仅由 \(0,1\) 构成的序列为”交错序列“,当且仅当序列中没有相邻的 \(1\) (可以有相邻的 \(0\))。

对于一个长度为 \(n\) 的交错序列,统计其中 \(0\)\(1\) 出现的次数,分别记为 \(x\)\(y\)。给定参数 \(a,b\),定义一个交错序列的特征值为 \(x^ay^b\)

求所有长度为 \(n\) 的交错序列的特征值的和,对 \(m\) 取模。

\(1\le n \le 10^7,0\le a,b\le 45,m\le 10^8\),且 \(m\) 为质数。

先将式子化简一下,因为 \(x+y=n\)

\[\begin{align*} x^ay^b&=(n-y)^ay^b \\ &=\sum_{i=0}^a \binom n i n^{a-i}(-y)^iy^{b} \\ &=\sum_{i=0}^a(-1)^{i}\binom n i n^{a-i}y^{b+i} \end{align*} \]

所以我们要求对于 \(i\in [0,a+b]\) 求出所有合法序列中 \(y^i\) 的和。

\(f_{i,j,0/1}\) 表示前 \(i\) 位,最后一位为 \(0/1\)\(y^j\) 的和。

考虑下一位填什么,若填 \(0\),那么可以从 \(0/1\) 转移,若填 \(1\),那么只能从 \(0\) 转移。

\(0\) 的话直接加起来就行了,考虑填 \(1\) 如何转移,\(y\) 会变成 \(y+1\),将 \((y+1)^j\) 展开

\[(y+1)^j=\sum_{k=0}^j\binom j k y^k \\ f_{i+1,j,1}=\sum_{k=0}^j\binom j k f_{i,j,0} \\ f_{i+1,j,0}=f_{i,j,0} + f_{i,j,1} \]

然后直接矩乘就行了,\(0/1\) 的话就开两倍。

CF797F Mice and Holes

\(f_{i,j}\) 表示前 \(i\) 个老鼠进前 \(j\) 个洞的最小距离,转移时枚举后面有多少老鼠进第 \(j\) 个洞,单调队列优化。

P1399 [NOI2013] 快餐店

基环树,从快餐店出发到所有点的最短路一定有一条环上的边没有经过,枚举一条环上的边删掉,求树的直径的最小值即可。

AGC009D Uninity

原题转化为,给树上每个点赋一个权值,满足任意两个相同权值的点之间存在比这个权值大的点,求权值的最大值最小是多少。

贪心从叶子往上取每个点能取的最小值,用 bitset 记录不能选的集合。

P4285 [SHOI2008]汉诺塔

\(g_{i,j}\) 表示在第 \(j\) 根柱子上有第 \(i\) 个盘子,到最终状态会到哪根柱子上,初值 \(g_{1,j}\) 由输入得到。

\(f_{i,j}\) 表示把第 \(i\) 个盘子从第 \(j\) 根柱子上移动到第 \(g_{i,j}\) 根柱子上需要几步。

转移时大力分类讨论。

CF704C Black Widow

连边,每个点的度数 \(\le 2\),会形成若干环和链,对于不同的连通分量,可以分别计算出异或值为 \(0/1\) 的方案数。

考虑链的情况,设 \(f_{i,j,k}\) 表示前 \(i\) 个点,第 \(i\) 个点值为 \(j\),已知表达式值的异或和为 \(k\) 的方案数,转移时枚举 \(i-1\) 的值即可。

对于环的情况,先固定一个点,然后从这里破环为链,最后答案取这个点固定的值。

P4564 [CTSC2018]假面

对于「锁定」技能,考虑每个位置分开算,\(f_{i,j}\) 表示单位 \(i\) 还有 \(j\) 点生命值的概率,最后对于单位 \(i\) 答案为 \(\sum j\times f_{i,j}\)

对于「结界」技能,考虑分开计算命中单位 \(i\) 的概率,枚举除 \(i\) 以外存活的单位数 \(j\),记这个概率为 \(p_j\),设当前 \(i\) 存活的概率为 \(a_i\),那么答案为

\[a_i\sum_{j=0}^k\frac{p_j}{j+1} \]

\(g_k\) 表示存活 \(k\) 个单位的概率,\(O(n^2)\) dp 很平凡,但是要对每个单位 dp 一遍,无法接受。

我们需要的是除 \(i\) 以外所有单位的概率,那么我们可以先求出所有单位的,然后把 \(i\) 删掉,写出转移矩阵不难做到 \(O(n)\)

总复杂度为 \(O(Cn^2+Qm)\)

UOJ140 被粉碎的数字

数位 dp,\(dp_{i,j,p,q}\) 表示从低到高前 \(i\) 位,此时 \(f(x\bmod 10^i)-f(k\times x\bmod 10^i)=j\),进位为 \(p\)\(q\) 表示 \(x\) 是否大于 \(R\)

转移时枚举第 \(i+1\) 位为 \(d\)

\[dp_{i+1,j+x-(p+k\times x)\ \bmod\ 10,(p+k\times x)/10,d=num_{i+1}?q:[d>num_{i+1}]}+=f_{i,j,p,q} \]

P7093 [CERC2014]Can't stop playing

如果存在,那么最终的序列一定是单峰的,并且和为 \(2^k\),记 \(L\) 为峰左边的和,\(R\) 为峰及其右边的和,放入第 \(i\) 个数后和为 \(S_i\)

\(f_{i,L}\) 表示放入第 \(i\) 个数后能否达到左边和为 \(L\),右边和为 \(S_i-L\) 的状态,这里保证 \(L<\dfrac {S_i} 2\)

转移时分类讨论即可,特殊地,当序列单调递增(减)时,两边都可以加入。输出方案就令 \(f_{i,L}\) 为从 \(L'\) 转移过来的。

P2476 [SCOI2008]着色方案

观察到每种颜色能涂的木块数很少,并且两种能涂的木块数相同的颜色是完全等价的,考虑记录能涂 \(i\) 个木块的颜色有多少,还需要记录上一个木块的颜色(这里也不需要真的记颜色,只需要知道能涂的木块数),状态数 \(15^5\times 6=4556250\)

转移时枚举颜色能涂几个木块,如果上一个涂的能涂 \(k\) 块的,那么这个涂能涂 \(k-1\) 块的颜色数需要 \(-1\),记忆化搜索优化。

P3648 [APIO2014] 序列分割

不难发现,切割的顺序对答案没有影响,那么直接 dp,令 \(f_{i,j}\) 表示序列前 \(i\) 项,切了 \(j\) 次的最大得分。

\[ f_{i,j}=\max_{k\in[1,i-1]}\{f_{k,j-1}+(sum_i-sum_k)\times sum_k\} \]

将两维交换一下,并把第一维滚动数组优化掉,然后斜率优化即可。

P2470 [SCOI2007]压缩

方便起见,我们约定每一段都由 M 开头,这样最后答案 \(-1\) 即可。

由于不能压缩已经压缩过的字符串,所以我们需要知道某个子串是否压缩过。记 \(s[l,r]\) 表示字符串 \(s\)\([l,r]\) 组成的子串,令 \(f_{i,j}\) 表示 \(s[l,r]\) 压缩后除了开头的 M 外没有 M 的最短长度,\(g_{i,j}\) 表示 \(s[l,r]\) 压缩后由多个 M 的最短长度。

\[f_{i,j}\leftarrow f_{i,k}+1\quad (s[i,k]=s[k+1,j]) \\ f_{i,j}\leftarrow f_{i,k}+j-k \\ g_{i,j}\leftarrow \min(f_{i,k},g_{i,k})+\min(f_{k+1,j},g_{k+1,j}) \]

AGC026E Synchronized Subsequence

先将原序列分段,每段内 \((a,b)\) 都完全匹配并且长度尽量小,这样每段可以单独处理然后单调栈求字典序最大的串。不难发现每一段内匹配的 \((a,b)\) 的相对位置是相同的。

发现串里只有 \(a,b\),所以要尽量让 \(b\) 靠前,进行分类讨论:

  1. 当该段内第一个为 \(a\) 时,由于每对 \((a,b)\) 的相对位置都是 \(a\) 在前 \(b\) 在后,如果保留了 \(a\),那么下一个保留和这个 \(a\) 匹配的 \(b\) 是最优的,所以保留的串形如 \(abab\dots\),求最长的长度即可。
  2. 当该段内第一个为 \(b\) 时,相对位置为 \(b\) 在前 \(a\) 在后,这时候要找到极长的 \(bb...b\),并且有个贪心,就是找到极长连续的 \(b\) 后后面的都保留,因为 \(b\) 在前 \(a\) 在后,多保留一对会更优。

最后单调栈处理一下就行了。

CF441E Valera and Number

观察到操作次数非常少,就导致 \(+1\) 的影响不会很大,具体来说,只会影响后 \(8\) 位。

\(f_{i,s,j,k}\) 表示 \(i\) 次操作,后 \(8\) 位状态为 \(s\),第 \(9\) 位及前面连续 \(j\) 位都是相同的,第 \(9\) 位为 \(k\) 的概率。

转移时分讨 \(\times 2\)\(+1\),注意一下进位即可。

最后统计答案枚举后 \(3\) 维。

posted @ 2022-07-01 22:29  Acestar  阅读(136)  评论(0编辑  收藏  举报