动态规划题目

状压 dp 进阶

高进制状压

一般是地图类,一次操作影响 \(\ge 3\) 行。一般采用行内 dfs 方式。
也有对于每个点需要有多种状态的。

P7689 [CEOI2002] Bugs Integrated,Inc.

如果是 \(3 \times 2\),那么第一行设为 \(2\),第二行 \(1\),第三行 \(0\) 就解决了。
如果是 \(2 \times 3\),第一行 \(1\),第二行 \(0\)
于是我们强制钦定 \(2\) 下面填 \(1\)\(1\) 下面填 \(0\)
行内 dfs 转移即可。

P2704 [NOI2001] 炮兵阵地

放炮的地方设置为 \(2\),然后依次为 \(1\) \(0\),只有 \(0\) 旁边才能放 \(2\)

P3959 [NOIP2017 提高组] 宝藏

可以很显然的状压,这里需要注意的是必须按层数转移,可以二进制但需要处理很多函数。这里采用三进制,\(0\) 表示未扩展,\(1\) 表示之前层的,\(2\) 表示最新层扩展出来的。显然只有 \(1\) 才能转移。
这里注意几个细节,每次扩展为 \(1\) 的最高位即可,如果扩展完,把这一位设置为 \(2\),虽然不符合定义,但这样显然是合法的。如果要进入下一层,把所有 \(2\) 变为 \(1\) 即可。

UVA1412 基金管理 Fund Management

有一种做法就是对于每个状态设置为 \(9\) 进制数,然后编码解码一下。不过太慢了,会超时。
有一个技巧就是对于一些我们发现合法状态很少的状压 dp,可以事先预处理出可能的状态进行编号以及预处理它们之间的转移。

状压杂题

QOJ8005. Crossing the Border

很显然的暴力,我们将 \(b_i\) 升序排序,然后按照集合右端点从小到大加入保证方案不重,这样子复杂度是 \(O(3^n)\)

考虑利用 \(\sum a_i \le m\) 的这个约束,可以想到类似 meet-in-middle 的思想,我们将集合分为两部分,每部分 \(\frac{n}{2}\) 个二元组,然后进行匹配。

我们可以枚举一个中间状态 \(LR\),表示从左边选出 \(L\),从右边选出 \(R\),在这个基础上匹配。我们可以从 \(L\) 的子集内选取集合转移到 \(R\) 的超集之中。假设 \(L'\subset L\)\(R \subset R'\),那么我们需要做的就是 \(L'R \to LR'\)。同样由于每次我们要加入包含最大右端点的集合,所以 \(L'\) 应该不含 \(\operatorname{maxbit}(L)\),这样新加入的部分 \(L-L'\) 满足最高位最新加入。\(R\) 同理。然后利用 \(\sum a_i \le m\),我们可以事先对于 \(L'\)\(R'\) 按照 \(\sum a_i\) 排序,然后只需要枚举 \(L'\),同时对于 \(R'\) 双指针即可。

超集和子集需要预处理,并且排序。

这样子我们相当于枚举了 \(L\) 及其子集,这是 \(O(3^{\frac{n}{2}})\) 的。同时枚举了 \(R\),这是 \(O(2^{\frac{n}{2}})\) 的。所以总的复杂度为 \(O(6^{\frac{n}{2}})\)

UVA1252 20个问题 Twenty Questions

本质上就是询问一个最小集合 \(s\) 使得有 \(n-1\) 个物品不包含这个集合。
这题有点难搞的原因是一方面是让我们最小化,另一方面是让我们考虑在最劣情况下的答案。是在最坏情况下尽可能小。其实很简单,只需要在 \(\min\) 里面套一个 \(\max\) 就可以了。设出状态 \(dp_{s1,s2}\) 表示已经询问了集合 \(s_1\),目前确定了物品所具备的特征为 \(s_2\),还需要至少多少次可以求出答案。

\[dp_{s,a}=\min\{ dp_{s,a},\max\{dp_{s+\{k\},a},dp_{s+\{k\},a+\{k\}}\}+1\} \]

树形 dp

P10220 [省选联考 2024] 迷宫守卫

首先注意一个要点,就是满二叉树的树高是 \(\log n\) 级别的,这使得后续复杂度有保障。

最优化排列思路一般是逐位确定,Alice 首先应该最优化第一位,同时最小化代价,可是我们似乎不太方便直接找到最大的满足条件的第一位。那么不妨二分下 \(x\),我们需要封堵 \(< x\) 的所有位置,直接 dp 一下最小代价看看是否当前能承受即可。

\[dp_u=dp_{lc}+\min(dp_{rc},w_u) \]

注意一下,因为我们要求 Alice 要提前设置好所以为了封堵 \(x\) 左右边都需要设置,所以这里是 \(+\) 而不是 \(\max\)。同时将题目中的提前设置转化为这里的动态设置也值得思考。

确定了第一位 \(P_1\) 之后,Bob 必然是直接奔向 \(P_1\) 所在叶子节点,然后退出来进行其他访问。由于是要按照类似 dfs 顺序访问,因此 Bob 活动在访问完第一个子树后似乎受到了限制,没法全局跑来跑去了,但是我们仍然可以对于 Bob 下一次进入的子树进行二分 \(+\) dp,这样就完美解决了。

还有几个细节,算出左右边的代价之后我们不要着急布置,因为目前的费用肯定是可以支付左右代价的,我们等进入对应位置之后再布置。同时我们发现使用 \(w\) 是有点亏的行为,所以计算的时候虽然是取 \(\min(w_u,f_{rc})\),但是实际我们发现如果剩余费用可以支付 \(f_{rc}\),那么就不用 \(w_u\)了,因为 \(f_{rc}\) 这么多费用在进入子树内是必然要被用掉的。还有一点思考就是提前布置其实违反了子问题一致性结构。

P7091 数上的树

我们可以生成一个 \(d_i\) 序列表示 \(n\) 的各个约数,其个数其实不超过 \(23327\) 个。对于禁用情况,我们直接不让其在这个序列中出现就行了。

可以考虑树形 dp,设 \(f_i\) 表示 \(d_i\) 为根的时候的最小代价。我们发现转移的时候那个 lca 的贡献不太好处理。思考一下性质,对于一个数,以它为根的树的大小是一定的。令 \(g_i\)\(d_i\) 的质因子个数的两倍减一,\(g_i\) 就是 \(d_i\) 子树的大小。于是我们 dp 转移就很好办了,每次枚举 \(d_j\times d_k=d_i\) 转移即可。发现固定了 \(j\),那么 \(k\) 唯一,如果直接用 map 记录需要多一个 \(\log\),可以用双指针优化。

闵可夫斯基和

ZROI2834.念念不忘

树上背包 dp,考虑 \(dp_{i,j}\) 表示 \(i\) 子树放了 \(j\) 个点,我们发现这是凸的。也就是 \(dp_{i,j}-dp_{i,j-1}\) 递增。

我们可以用启发式合并差分,取最小的差分即可。原理是如果差分不递增的话,我们优先选小的差分会造成某个子节点的第一差分没选,选了第二差分。这显然是不行的,因为我们发现如果要选一段差分,必须从头选,只有最小化且差分递增的时候才能用。

P9962 [THUPC 2024 初赛] 一棵树

双栈结构

LOJ6515.「雅礼集训 2018 Day10」贪玩蓝月

如果是离线那就线段树分治。
如果强制在线的话,我们可以类似 deque 的实现,

\(\omega\) 的仙人掌

求最短区间 \([l,r]\) 使得 \((w_i,v_i)~(l\le i \le r)\) 做完背包后权值为 \(w\) 的代价小于 \(v\)\(n\le 10^4,w_i~w\le 5\times 10^3,v_i\le 2\times 10^5,v\le 10^9\)

\(\operatorname{Trick}\): 双栈优化 DP。需要动态加减不可减信息的时候,我们发现线性次数加法是可以接受的,可以考虑这么一个双栈结构。
比如在本题中,我们发现区间满足双指针性质,但是背包不太好减,观察到值域很小,于是我们可以维护多个背包,然后似乎也有点难办,因为第 \(i\) 个点的背包是依赖于 \(i-1\) 个点的所以删掉第 \(i-1\) 个点的背包依然无法消除第 \(i\) 个点背包内 \(i-1\) 的痕迹。我们不妨维护两个栈 \(s_1\)\(s_2\)\(s_1\) 从上到小维护的是 \(r~r-1~r-2...p\)\(s_2\) 维护的是 \(l~l+1...p+1\),其中 \(l\)\(r\) 就双指针的两个位置。\(s_1\) 为正着做一遍背包,\(s_2\) 为倒着做一遍背包,这个样子我们每次移动 \(l\) 指针,只需要弹出 \(s_2\) 栈顶即可,每次移动 \(r\) 是不断往 \(s_1\) 中加。如果 \(s_2\) 被清空,我们就把 \(s_1\) 中的所有二元组弹出来依次压入 \(s_2\) 中,这样子每个二元组入栈两次,时间复杂度 \(O(nw)\)

区间约束类

通常为给出若干的区间,要求必须满足区间条件或者完成区间任务可以加分。
一般做法: 以区间边界为转移点进行转移,合并限制发现会有几个关键点,于是 \(pos_i\) 表示上一个关键点最晚出现的位置 (\(pos_i < i\))。\(f_{i,j}\) 表示在位置 \(i\) 上一个关键点为 \(j\)。注意维度可以压缩,比如例 1 直接压掉了第一维。例 3 强制钦定转移位置压掉了第二维。

CF1327F AND Segments

1010101010101001 0 01
1100100100101001 0 01 \(\gets\)
1010101010101000 0 00
1001001011011010 1 01 \(\gets\)
1010110101010101 0 11

考虑到二进制的独立性,所以可以拆位统计。这题拆位又碰上区间,二维限制有点难想。可以画个图,那一竖列为拆出来的一位。 两个箭头代表区间左右端点,加粗的几个数字就是一条限制拆位后约束的数字。这样就一目了然了。

按位与的性质得出,若区间与为1则必须全部为 \(1\),与为 \(0\) 的话区间至少一个 \(0\)。看起来可以先将 \(1\) 填入,再考虑 \(0\) 的放置,然后计数原理乱搞。但是限制为 \(0\) 的区间有重叠就很恶心。

那就只能 \(dp\) 了。二维 \(dp_{i,j}\) 表示在 \(i\) 位时,上一个 \(0\) 填在第 \(j\) 位的方案数。

  • \(j<pos_i\) \(~dp_{i,j}=0\)
  • \(pos_i \le j< i\) \(~dp_{i,j}=dp_{i-1,j}\) (上一个 \(1\)\(j\) 处,故 \(i\) 处只能填 \(0\) )
  • \(j=i\) \(~dp_{i,j}=\sum\limits_{k=pos_i}^{i-1}dp_{i-1,k}\)

看似复杂度不行,但是列出状态转移方程后我们可以惊喜的发现并不需要第一维,直接维护段和即可。这就启发我们有的时候先大胆列出式子哪怕复杂度爆了,后面再优化。

P4229 某位歌姬的故事

P9871 [NOIP2023] 天天爱打卡

AT_dp_w Intervals

邻相依赖的计数

https://www.cnblogs.com/chroneZ/p/17938137

P9197 [JOI Open 2016] 摩天大楼

双倍经验:P2612 [ZJOI2012] 波浪
我们将排列放在平面上,可以画出折线。总和显然是纵坐标之差。考虑扫描线,看扫描线与折线交点。当扫描线移动的时候,可能会让相邻两个交点消失,或者多出两个交点。

✖ 2 +1

开关区间法

CF626F Group Projects

输入顺先显然是不重要的,我们可以对 \(a_i\) 排序。然后最暴力地列出状态, \(f_{i,j,k}\) 表示目前考虑到了第 \(i\) 个数,分了 \(j\) 组,总和为 \(k\) 的方案数。但这显然不好转移。思考一下本题难点在于

字符串相关

ABC240Ex Sequence of Substrings

暴力做法就是把所有子串抽出来进行字典序排序,这个可以在字典树上完成。

然后每次转移直接暴力找到位置在它之前的,排名比它小的子串进行转移,时间复杂度 \(O(n^2\log n)\)

子串数量已经是 \(O(n^2)\) 了,如果直接对于全体子串为状态做转移必然是不可行的。这启发我们实际有效状态应该很少,观察数据范围大概是要求出现 \(\sqrt n\) 的东西。

之前做字符串题遇到过一个结论就是对于若干个长度和为 \(n\) 的字符串,不同的长度种类的上限是 \(O(\sqrt n)\) 的。本题选取若干个原串不相交的子串也是同理。但是这还是难以完成本题,不过至少提示了我们可以从长度下手。这里要结合字典序的性质下手,如果我们最终选取的子串中相邻两个串的长度差 \(\ge 2\),那么对于长的那个串可以缩短为长度差等于 \(1\),依然不改变字典序大小关系。

于是我们就可以发现,所选取的子串长度最大也是 \(2\sqrt n\),我们把所有满足要求的子串取出来跑最开始说的暴力算法即可。

ACAM

P5319 [BJOI2019] 奥术神杖

乘积式取 ln,转化为和式的平均数,这可以用 0/1 分数规划解决。
然后在 AC 自动机上跑 DP 就可以了。设 \(dp_{i,j}\) 表示前 \(i\) 个字符,到了 AC 自动机的节点 \(j\) 所得到的最大权值。

杂题

【NOIP Round #6】抉择

感觉这种优化到尽头还无法解决的 \(dp\) 就是要挖掘一下性质。
部分分挺简单的,但是正解思路其实和特殊性质最后一档很想,二者的思想都其实是选更多的大概率会更优一点。我们来分析一下为什么不多选,比如选了 \(a_j\)\(a_i\),我们非要在其中插入的一个 \(a_k\),那么可能 \(a_k\) 的很多位都为 \(0\),导致我们损失了一些高位。这里给出一个结论也就是只要 \(a_i~and~a_j\)\(a_i~and~a_k\) 的最高位相同即可,很明显最高位大于其他位之和。所以我们只需要对于每一位保存最近的即可。注意别忘记考虑按位与为 \(0\) 的情况。

P9870 [NOIP2023] 双序列拓展

一道优化 dp 的好题目。
下面只讨论 \(f_i>g_i\),方程是显然的
\(dp_{i,j} \gets dp_{i,j-1} \cup dp_{i-1,j} \cup dp_{i-1,j-1}~~(a_i>a_j)\)
\(dp_{i,j}=0~~(a_i \le a_j)\)
时间复杂度 \(O(qnm)\)。这显然不太好压缩维度或者用数据结构来优化了。
那就需要寻找 dp 性质了。
方法一:将 dp 方程转为有组合/几何意义的东西
这里可以变成走方格,从 \((1,1)\)\((n,m)\)。我们发现走不到终点的充要条件是有一个围了一圈的封堵。这个直接可以双指针来优化。

方法二:bool 型 dp 提取状态到 dp 值中。
可是很多类似的题目我们都可以发现对于 \(dp_i\) 并不是需要越大越好,所以不能只记录一个最大的 dp 值。每题的解决方法都不同,有的题目是观察出一维度固定另一维度合法的是一个区间,本题是用到了可回退的思想。

我们先最大化 \(dp_i\)

如果 \(a_i \le a_{i+1}\),那么显然是可以的 \(dp_{i+1}\)\(dp_i\) 的基础上继续扩展,即找到一个 \(b_j \ge a_{i+1}\)

如果 \(a_i > a_{i+1}\),那么不能继续扩展,我们可以贪心地进行回退,退到第一个 \(b_j<a_{i+1}\),此时对于 \(a_{i+1}\) 是满足条件的,可以证明对于 \(\le i\) 的位置也是满足条件的,因为 \(a_i > a_{i+1}\),所以 \(a_i\) 对位 \(b_j\) 就可以了。时间复杂度 \(O(qn\log m)\)

考虑去掉 \(\log\),我们发现每次回退又向前很重复。这里有一个观察,如果在位置 \(i \to i+1\) 的时候发生了回退了,如果 \(a_j>a_i\) 那么本次回退是无效的,因为 \(a_j\) 会比 \(a_i\) 走得更远。于是我们发现只有前缀 \(\max\) 才会对答案产生向前的贡献且可以抵消之前的回退。回退的时候只需要检查 \(a_i\) 是否大于 \(b\) 序列的前缀 \(\min\) 即可。需要注意的一点是我们虽然是免去了回退但是后面必须有点可以恢复过来。这就是要求序列的末尾必须是前缀 \(\max\) 特殊性质可以满足这点。如果不是呢,将序列从 \(\max\) 处分成两半,前一半顺着扫,后一半倒着扫,这样可以满足了。

P6847 [CEOI2019] Magic Tree

首先是二维 dp,用 \(f_{u,i}\) 表示在 \(u\) 节点,其所有子节点在 \(\le i\) 时刻被切断所得到的最大价值。\(i \ge d_u\) 时候,\(f_{u,i}=\sum\limits_v f_{v,d_u}+val_u\)\(i \le d_u\)\(f_{u,i}=\sum\limits_v f_{v,i}\)。注意这里的 \(f\) 是前缀最大值因为定义是 $ \le i$。
但是这里是二维的,复杂度显然不行,一般这种二维 dp 想要优化的话,一般就是第二维通过转化为图像或者线段树合并啥的或者更难点就是找性质。
这里我们可以发现其实有用的 \(f\) 值其实很少,因为分界点级别其实是 \(O(n)\),均摊到 \(O(n)\) 个节点上就是 \(O(1)\) 的。于是我们可以使用动态开点并且回收空间线段树(记录节点 \(\max~\min\)),或者 map 启发式合并。
如果是 map 的话不方便整体修改,可以考虑记录 \(f\) 数组的差分,注意差分合并的时候是可以直接 \(+\) 的。不难发现 \(f\) 是不降的,于是前缀 \(\max\) 可以在每次修改后从修改位置起不断比较 \(w_u\) 与目前差分大小,如果目前差分小了就直接删除 \(f_{u,i}\),同时 \(w_u\) 减去 \(f_{u,i}\) 继续往后比较。

CF1476F Lanterns

由于目标是全局覆盖,所以我们断断续续地覆盖最后再用后面来覆盖前面空缺的是很难用状态表示的。于是考虑记录一个连续段 \(dp_i\) 表示到第 \(i\) 个灯笼能覆盖地最长前缀有多长。可是我们如果在中间一段出现一个位置的最优选择是向后,但是他前面没有补齐怎么办呢?等后面补齐的时候这个位置再往前延展。也就说对于当前的 \(i\) 可以找到一个最靠前的位置 \(j\) 使得 \(i\) 可以覆盖到 \(f_j+1\),于是我们便可以在 \((j,i)\) 之内的位置向前延展了。

P3643 [APIO2016] 划艇

值域过大考虑离散化,然后按照原有端点,分成很多小端。设 \(f_{i,j}\) 表示第 \(i\) 个数的值域在 \([l_j,r_j]\) 之间。考虑转移的时候,\(k \to i\) 如果值域区间不同还可以转移,但是如果他们在同一个值域区间内就不好办了。

那就只能使用大招,强行钦定 \(k \to i\) 的转移是不同区间的,同时 \(k+1,k+2...i\) 都落在 \([l_j,r_j]\) 之间或者取值为 \(-1\)。小细节:由于状态设计要求这里的 \(c_i\) 不能取 \(-1\)。设一个有 \(m\) 个数待选择,那么方案数是 \(\begin{pmatrix} m+Len_j-1\\ m \end{pmatrix}\)

\[f_{i,j}=\begin{cases}\sum\limits_{k=0}^{i-1}\sum\limits_{z=0}^{j-1} f_{k,z} \times \begin{pmatrix} m+Len_j-1 \\ m \end{pmatrix} & [l_j,r_j] \subseteq [a_i,b_i] \\0&otherwise \end{cases}\]

组合数可以线性递推。

我们仔细观察一下求和式子,似乎可以前缀和优化。但是一定要小心,那个 \(m\) 其实是和 \(k\) 有关的,所以我们只能对于第二维进行前缀和优化。

同时可以用前缀和优化,发现第二维求和与 \(i\) 无关,于是我们可以设 \(s_{i,j}=\sum\limits_{z=0}^{j-1} f_{k,z}\)。那么 \(f_{i,j}=\sum\limits_{k=0}^{i-1}s_{i,j}\times \begin{pmatrix} m+Len_j-1 \\ m\end{pmatrix}\)

注意细节,为了前缀和方便,我们将 \(j\) 提取到第一维进行枚举。同时,第二维枚举 \(i\) (倒序)也有利于使得 \(m\) 每次只增加 \(1\),便于线性递推组合数。

时间复杂度 \(O(n^3)\)

P7606 [THUPC2021] 混乱邪恶

综合了各种技巧的背包题。

设计状态 \(f_{0/1,l,g,x,y}\),第一维滚动数组,后面分别表示 \(L,G\) 目前积累的值,然后就是坐标。可以发现每次移动的 \(\pm 1\),可以用 bitset 优化。然后还有一个经典结论就是随机游走的期望最大移动距离是 \(\sqrt V\) 级别的,这样子坐标维度直接开 \(2\sqrt n\) 级别就行了。

CF1667D Edge Elimination

构造题目依然可以考虑 \(dp\)。当信息只与子树有关所以可以树形 \(dp\),所以本题可以记录与父亲有关状态,使得子树独立。设 \(f_{u,0/1}\) 表达 \(u\)\(fa\) 删边的时候,有偶/奇数条子边。根据子节点的 \(f_{v,0/1}\) 值种类分为 \(4\) 种。如果全为 \(0\),那就无解。对于一个 \(1\) 的情况,显然是在 \(u\) 剩奇/偶数的时候交替删。如果有两个 \(1\),这些东西可以用来平衡。

P6891 [JOISC2020] ビルの飾り付け 4

首先 \(O(n^2)\)\(dp_{i,j,0/1}\) 是显然的。

CF1444E Finding the Vertex

P5912 [POI2004] JAS
Uninity
貌似这种交互库动态构造的题目都是要先用 \(dp\) 求出最优询问策略。
询问 \((u,v)\),根据回答,在 \(u/v\) 的子树中寻找。寻这个过程就是一个边分治的过程。我们需要最小化边分树每条边的深度。当边深度满足条件的时候,边分树和边深度集是双射的。需要满足的条件就是对于任意两条权值相等的边,他们公共祖先的边需要有边权值小于他们。

[AGC058B] Adjacent Chmax

P8392 [BalticOI 2022 Day1] Uplifting Excursion

P8175 [CEOI2021] Tortoise

CF809D Hitchhiking in the Baltic States

朴素的状态以及转移显然不行,我们可以联想到 LIS 的更优做法,那就是动态维护长度为 \(i\) 的 LIS 结尾数字最小。

本题也可以沿用该思路,设出 \(f_i\)。那么考虑加入一个数 \(\in [l_i,r_i]\),这个时候就需要找到所有的 \(f_i<r\),对应的 \(f_{i+1}= \max(f_i+1,l_i)\)。化简一下,也就是找到最大的 \(j\) 使得 \(f_j<l\),此时 \(f_{j+1}\) 必然大于等于 \(l\),故\(f_{j+1}=l\),其余的 \(l \le f_i<r\)\(f_{i+1} \gets f_i+1\)

必然是数据结构维护,但是第一感得到的线段树好像并不能有效完成第二个操作。我们发现第二个操作中的 \(f_{i+1}\) 都是强相关于 \(f_i\),于是可以把 \(f_i\) 右移然后区间 \(+1\),那么空出来的那个位置怎么办,其实正好用 \(f_{j+1}\) 来填补。然后末尾多出来一位怎么办,如果本来就可以让答案序列增长一位那么就不用管,否则在后面再删掉一个节点就行了。

UVA10934 装满水的气球

首先确定策略,如果只有一个球那么我们显然只能从下往上一层一层摔直到确认到位置。如果多一个球,那么实验次数就减小了。因为我们可以在某次直接到更高层,然后试错。考虑 dp,由于我们不知道答案楼层是什么,所以楼层不应该放在状态里面。

\(dp(i,j)\) 表示用 \(i\) 个球,实验 \(j\) 次可以确定最大高度。

本次测试的楼层应该是 \(dp(i-1,j-1)+1\),因为如果本次气球破了,保证可以用剩下的操作次数和气球完成实验。我们要求的是最高楼层所以应该是要加上最好的情况,也就是气球没破,那就还能往上扩展 \(dp(i,j-1)\) 层。于是 $$dp(i,j)=dp(i-1,j-1)+dp(i,j-1)+1$$

  • 虽然是加起来,但是意思不是都要进行一次,其实是根据气球有没有破碎选择其中一种方式来继续询问。

每次询问就是找到一个最小的 \(i\),满足 \(f_{i,k}\ge n\),二分即可。时间复杂度 \(O(T\log K)\)

ABC221G Jumping sequence

发现每一步都选择一个方向都太难搞了,考虑 \((x,y) \to (x+y,x-y)\) 旋转一下坐标系,那么每一个方向就是独立了的,我们每次可以在每个方向上选择前进还是后退。于是对于每个维度都有
选择 \(p_i \in \{-1,1\}\),使得 \(\sum p_i\times d_i=T\),其中 \(T=A+B,A-B\)
这很像背包的形式,两边同时加上 \(\sum d_i\),再除以 \(2\),得
选择 \(p_i \in \{0,1\}\),使得 \(\sum p_i \times d_i=\frac{T+\sum d_i}{2}\)
这是一个 0/1 背包的形式,可以用 bitset 辅助完成。

posted @ 2024-02-18 14:34  Mirasycle  阅读(8)  评论(0编辑  收藏  举报