dp 题记录

CF1716D Chip Move

给定两个数 \(n,k\),问从 \(0\) 开始,第 \(i\) 步只能走 \((k+i−1)\) 的倍数,问分别走到 \(x\in[1,n]\) 的方案数,对 \(998244353\) 取模。


考虑 \(dp\),设 \(dp_{i,j}\) 表示当前在 \(i\),走了 \(j\) 步的方案数。

\(dp_{i,j} \rightarrow dp_{i + x, j+ 1}\)

初值为 \(dp_{1,0} = 1\),暴力转移复杂度为 \(n^2 \sqrt{n}\)

其中 \(x\)\((k + j - 1)\) 的倍数,额,肯定是外层循环枚举走到了第 \(j\) 步,然后那么 \(k -j + 1\) 是已知的。

考虑一个数在这种情况下会加上哪些数,维护一个前缀和应该就好了?

你这个看上去,就不能走太多步的样子啊其实。

因为 \(k + (k + 1) + (k + 2) + \dots + (k + m) \leq n\) 这个 \(m\) 始终小于等于 \(\sqrt{n}\),那么就可以根据这个做了。

首先滚动数组,之后我们每次维护一个魔改的前缀和就好了。

CF1701E Text Editor

给定两个字符串 \(S, T\),初始时光标在串 \(T\) 尾部,你可以进行以下操作:

  • \(\texttt{left}\):将光标向左移动一个字符,如光标在字符串最左侧则不移动。

  • \(\texttt{right}\):将光标向右移动一个字符,如光标在字符串最右侧则不移动。

  • \(\texttt{home}\):将光标移动到字符串最左侧,如光标在字符串最左侧则不移动。

  • \(\texttt{end}\):将光标移动到字符串最右侧,如光标在字符串最右侧则不移动。

  • \(\texttt{backspace}\):将光标左侧的第一个字符从字符创中删除,如光标在字符串最左侧则不删除任何字符。

我们需要使 \(S\) 在做若干次操作后得到 \(T\),请你求出最小的操作次数。无解输出 \(-1\)


我草,不会啊,爬去看题解。

直观感受的话你肯定是用 leftbackspace 或者在中间某个时刻突然 home 然后 rightbackspace

那么,我们从前面和后面分别跑一遍 dp,然后枚举一个中间端点取答案的 \(\min\) 就好了。

跑的 dp 形如 \(dp_{i,j}\) 表示当前光标到了 \(s\) 的第 \(i\) 位,目前和 \(t\)\(1 \sim j\) 位匹配。

然后 \(dp_{i,j} = dp_{i - 1,j} + 2\) 是表示删除

然后 \(s_i = s_j\) 就可以不用删除,\(dp_{i,j} = dp_{i-1,j-1}+1\)

然后记录 \(fp_{i,j}\) 表示 \(s_{1\sim i}\)\(t_{1 \sim j}\) 匹配的最小值。

\(s_i = s_j\) 的时候且不用删除小于删除,那么 \(fp_{i,j} = fp_{i-1,j-1}\),就考虑不移动,否则 \(fp_{i,j} = fp_{i-1,j} + 2\)

倒着过来的时候删除是 \(+1\) 的。

然后最后前面和后面做一个匹配,一下就好了。

[HNOI2014]米特运输

一棵树,每个点有权值,要求修改点权值满足,每个点是儿子权值的和,同一个点的儿子权值相同。

要求最少的修改次数。


首先容易想到 \(n^2\),然后发现确定了一个点的值就确定了所有点的值。

设当前节点的值为 \(x\),那么此时根节点权值为 \(\prod_{fa} siz_{fa} \times x\),其中前面的连乘符号表示的是祖先的 \(siz\) 连乘。

那么我们记录下这个状态,然后取最多的为同一个数的就好了,因为这样修改的就最少。

这个部分的话,可以哈希,也可以取对数来求。

取对数的时候注意不能用桶判断相同,要手写 eps。

牛客小白月赛 53 F

\(n\) 个长度为 \(m\) 的二进制数,要求选出里面的一个子序列,满足每一位没有重复的 \(1\) 出现,求有多少种子序列的方案数


我们很容易想到一个复杂度为 \(n \times 2^m\) 的一个 dp,是 \(dp_{i,j}\) 表示考虑了前 \(i\) 个,以 \(j\) 结尾的满足要求的子序列方案数。

那么每次转移的时候也就是 \(dp_{i, a_i} += \sum_{k\&a_i =0} dp_{i-1,k}\),这里做的复杂度就是 \(n2^m\),然后同时每次还要继承一下上一次的状态,滚动数组一下就好了。

但是不能过,这里有一种降低复杂度的神奇方法。

我们默认这里的 \(m\) 为最大的 \(16\),令 \(g_{i,j,k}\) 表示考虑了前 \(i\) 个数,结尾的数的高 \(8\) 位为 \(j\),低 \(8\) 位和 \(k\) 按位与后为 \(0\) 的子序列的方案数。

对于一个数可以 \(x = (a_i >> 8),y = (a_i \bmod 255)\) 可以得到高位和低位。

那么我们首先模拟一遍 \(k\) 为高八位,然后如果 \(k \& x = 0\) 那么让 \(g_{i,x,y} \leftarrow g_{i-1,k,y}\),因为此时 \(g_{i-1,k,y}\) 表示 \(k\) 多对应 的那个低位也和 \(y\) 按位与为 \(0\),那么就有贡献。

设这部分的总贡献为 \(res\)

然后模拟一遍 \(k\) 为低八位,然后如果 \(k\& y = 0\),那么我们现在对应的状态其实就是 \(g_{i, x,k}\) ,那么我们此时对应的操作就是 \(g_{i,x,k} \leftarrow res\)

第三维度从来指的都不是低 \(8\) 位,而是低八位 \(\&\) 上他为 \(0\)

然后显然第一维其实是没有什么用的,于是我们就可以把他滚动掉。

这道题挺厉害的感觉。

Luogu P1272 重建道路

一颗 \(n\) 个点的树,然后要求删边,问,删边使得剩出一个大小为 \(p\) 的连通块要用的最小删边数量。


\(dp_{i,j}\) 表示当前在子树 \(i\),保留下一个含有他的大小为 \(j\) 的连通块最少要用的一个删边数。

初始化 \(dp_{x,1} = 0\),啥也不管,然后考虑往下接儿子。

你发现这,其实就是一个树形背包,就直接每次从儿子转移过来的时候,考虑 \(dp_{x,j} = \min \{ dp_{x,j} + 1, dp_{x, j - k} + dp_{to,k} \}\) 就好了,然后这里一个要注意的点是,里面那个 \(+1\) 一定要在每次循环枚举 \(k\) 之前就加了!

虽然我也不知道为什么会错,但是好像挺奇怪的,我思考了一下也不知道为什么。

复杂度树形背包写的正确的话就是 \(O(np)\) 的,我也不知道为啥 \(n \leq 150\)

CF873D Round Subset

不写题意了以后/难过。

注意到末尾 \(0\) 的个数之和 \(2,5\) 的个数有关,于是考虑状态 \(dp_{i,j,a,b}\) 表示前 \(i\) 个选了 \(j\) 个,有 \(a\)\(2\)\(b\)\(5\) 的时候是否合法。

空间不太行,所以压一下 \(dp_{i,j,a}\) 表示前 \(i\) 个选了 \(j\) 个有 \(a\)\(5\) 时最大的 \(2\) 的个数,同时第一维可以滚动掉,直接转移即可。

CF1710C-XOR Triangle

这题不会啊,感觉数位 dp 还是要重学一下。

容斥考虑不合法的三元组,然后用全部的三元组数目 \((n + 1)^3\) 减去不合法的数目就可以得到合法数目。

\(x = a\oplus b, y = b \oplus c, z = a \oplus c\),则有 \(x \oplus y \oplus z = 0\)

\(x + z \leq y, x + y \leq z, y + z \leq x\) 是违法的判定条件

\(x + y \leq z\) 等价于 \(x, y\) 的二进制对中没有 \(11\)\(z\) 中没有 \(0\)\(000\) 除外)

\(x + z \leq y\) 等价于 \(x, z\) 的二进制对中没有 \(11\)\(y\) 中没有 \(0\)\(000\) 除外)

\(y + z \leq x\) 等价于 \(y, z\) 的二进制对中没有 \(11\)\(x\) 中没有 \(0\)\(000\) 除外)

可以设 \(dp_{i, s1, s2}\) 表示还有 \(i\) 位没考虑,\(a,b,c\) 是否等于 \(n\)\(x,y,z\) 是否全 \(1\)(或 \(000\)),这个状态下的不合法数目。

\(s1\) 用三个位表示前三个状态,\(s2\) 用三个位表示后三个状态、

初始 \(dp_{n,7,7} = 1\),因为此时只有一种情况 \(a = b= c = 0\)

枚举 \(a,b,c\) 当前位的选取情况,假设是 \(s\),则 \(dp_{i+1,s1,s2}\)\(dp_i\) 的贡献如下:

如果 \(n\) 当前位为 \(0\),只能转移到 \(s1_next = s1\),此时要保证 \(s1 \& s = 0\), 不然说明有等于 \(n\) 前缀且当前选择 \(1\) 的情况,这样就大于 \(n\) 了。

如果 \(n\) 的当前位为 \(1\),则能转移到 \(s1_{next} = s_1 \& s\),计算出 \(s2_{next}\) 要通过前面的状态 \(s2\)\(s\) 计算。

P3713 [BJOI2017]机动训练

转换一下题意,题目中的机动路径假如有 \(sum\) 个,那么贡献 \(sum^2\),用组合意义转化一下,这个等价于两个人走同样的机动路径的方案数。

注意到,每个点移动的方向只会有一种,要么是左上,左下,右上,右下(类型一),要么是正上,正下,正左,正右(类型二)。

题目要求路径不能远离目标点,那么移动的方向一定是确定的,可以枚举方向。

所以可以设 \(dp_{a,b,c,d}\) 表示第一个人从 \((a,b)\) 出发和第二个人从 \((c, d)\) 出发,走的机动路径相同,沿着当前的方向走的合法方案数。

然后每次枚举方向,然后搜出能走的选择,在直接记忆化搜索路径,然后就可以求得答案,注意到,我们的正上,正下之类的,会被类型一给包含。

例如:正上的抉择被左上和右上能走的方向中包含,算了两次,于是容斥一下,对于类型一的答案全部加上,对于类型二的答案全部减去就好了。

P5307 [COCI2018-2019#6] Mobitel

卡老师题

首先傻逼 dp,直接设 \(dp_{i,j,k}\) 表示走到 \((i,j)\) 乘积为 \(k\) 的方案数,但是空间太大了,完全寄。

考虑将状态进行另一种转化,我们并不关心具体的值,而是关心一个相对的大小关系,也就是 \(\times\) 多少会比 \(n\) 大,设 \(dp_{i,j,k}\) 表示走到 \((i,j)\) 时,当前乘积 \(\times k\) 之后比 \(n\) 大的方案数。

本质不同的 \(k\) 的数量等价于是一个整除分块,于是只会有根号种,第一位滚动一下数组,可以开下空间。

然后刷表更新即可。

posted @ 2022-09-06 14:34  Pitiless0514  阅读(43)  评论(1编辑  收藏  举报