DP 做题记录
复杂DP
写在最前面的一些话。
DP 算是最常见的一种题目了,类型多样,很值得研究。
究其本质,DP 可以看做是在 DFA(有限状态自动机)上沿着转移边进行转移。考虑某种组合结构,所有满足某一条件的该结构作为元素组成一个解集,题意要求求出这些解的某种权值经过某种运算后的结果,DP 就是使得自动机在这些解上运行。这种情况下的 dp 大致有三类:最优化、计数、判定。在所有合法解不漏的前提下,它们需要满足的关键条件是:最优子结构、不计重、无后效性。
常见的 DP 结构有:
区间 DP,状态压缩 DP,树形 DP,背包 DP,概率/期望 DP,子集(SOS)DP 等。
通用的思考方法:
- 分析模型性质,简化模型,揭开模型“真面目”;
- 处理模型,找到可以用 dp 解决的对象;
- 明确转化后的问题,设计 dp 方式;
- 优化 dp。
第一步就是简化条件、模型、过程,让问题更简洁清晰。
第二步就是考虑模型的“要素”,一般是模型结构的组成部分、相关量或维度。一个模型包含多个要素,要素之间的关系就是题目条件和描述,以及导出的性质。
第三步就是设计状态。状态需要满足最优子结构、不计重、无后效性,接着是转移方式,有填表法、刷表法、记忆化搜索等方式。
最后就是优化。最基础的就是优化状态,然后就是前缀和优化,数据结构优化,倍增优化,决策单调性,CDQ 分治等优化手段。
CF123C Brackets
题意:括号数组是一个只有 “(” 或 “)” 两类字符的二维数组。括号数组中的合法路径只能从任意位置开始,向右或向下移动。如果一个 n×m 括号数组中从 (1,1) 到 (n,m) 的所有路径经过的字符构成的字符串均为可以完全匹配的括号序列,则这个括号数组可以被称作神奇数组。
现在定义一种比较神奇数组大小的方式,假设这两个数组分别为 a 和 b ,两个数组行列均相等,找到数组中满足条件 a i,j=b
i,j 的位置 (i,j) ,如果有多个这样的位置,则选择优先级编号最小的位置(数据中已给出),如果此时 a i,j= “(” ,则 a<b ,否则 a>b ,如果不存在这样的位置,则 a 和 b 相等。根据以上定义,请找出第 k 大的神奇数组。
思路:有性质就是因为所有路径都合法,那么对于任意的 \((i,j-1),(i-1,j)\) 应该是相同的。可以考虑从 \((n,m)\) 倒推来证明。
于是每一个对角线上的括号都是相同的,我们可以只用管外轮廓上的括号,于是就逐位确定,每次用 \(O(n^2)\) 的 DP 来求解。具体地,我们设 \(f[i][j]\) 表示在第 \(i\) 位时前缀和为 \(j\) 的方案数,于是就可以了。
复杂度 \(O(n^3)\)。
trick:分析性质,简化状态。
P4229 某位歌姬的故事
题意:有一个长为 \(n\) 的序列 \(a\),每个元素在 \([1,A]\) 中,给出 \(m\) 个限制,表示 \(\max\limits_{j=l_i}^{r_i}a_j=m_i\),求满足条件的 \(a\) 的个数。
思路:先离散化。对于一个区间,求出所有包含它的限制中最小的一个 \(minn_i\),然后对于每一种出现过的 \(m_i\) 进行 dp。
dp 时,首先取出所有 \(m_i=lim\) 的限制,然后找出所有 \(minn_i=m_i\) 的区间,然后根据每个限制包含的区间进行dp,设 \(f[i][j]\) 表示当前已经考虑到了限制 \(i\),上一个到达了上界的区间是 \(j\) 的方案数,然后根据上一个限制和这一个限制有没有交集进行分类讨论,并且如果一个区间可以不达上界,方案数是 \(lim^{len}\),否则是 \(lim^{len}-(lim-1)^{len}\)。
复杂度 \(O(Tm^2)\)。
trick:先确定每个元素的限制,再根据限制进行 DP。
CF1476F Lanterns
题意:有 \(n\) 个灯笼排成一排,每个灯笼可以照亮 \([i-p_i,i-1]\) 或 \([i+1,i+p_i]\),给出一种构造使得每个灯笼都至少被一个灯笼照亮。
思路:考虑 dp。设 \(f_i\) 表示前 \(i\) 盏灯笼可以照亮的最长前缀。
转移时,如果 \(f_{i-1}<i\),那么 \(f_i=f_i-1\);否则,第 \(i\) 盏灯笼可以向右或向左。
如果向右,那么 \(f_i=\max(f_{i-1},i+p_i)\);如果向左,那么找到最小的 \(t\) 使得 \(f_t\leqslant i-p_i-1\),则 \(f_i=\max(i-1,\max\limits_{j=t+1}^{i-1}j+p_j)\) 表示 \(i\) 向左,中间一段向右,那么只需求 \(i+p_i\) 的区间最大值,用 ST 表即可。复杂度 \(O(n\log n)\)。
trick:覆盖全集 \(\rightarrow\) 覆盖前缀。
P5336 [THUSC2016] 成绩单
题意:就是给出一个序列,每次从序列里抽出连续的一段,这一段的代价为 \(A+B(max-min)^2\)。
思路:一种很新的区间DP,删掉后会合并的DP。
这样状态要把端点加进去,例如加入右端点,于是设 \(dp[l][r][maxn][minn]\) 表示把 \(l\sim r\) 删完,与 \(r\) 相连的一段的最大值为 \(maxn\),最小值为 \(minn\) 的方案数。
设 \(f[l][r]=\min(g[l][r][maxn][minn]+a+b(maxn-minn))\),
于是有转移:
保留 \(r+1\),有 \(g[l][r+1][\max(maxn,a[r+1])][\min(minn,a[r+1])]\leftarrow g[l][r][maxn][minn]\)
取 \([r+1,k]\),有\(g[l][k][maxn][minn]\leftarrow g[l][r][maxn][minn]+f[r+1][k]\)
trick:如果普通的区间 DP 不适用,可以尝试添加状态。
[AGC036D] Negative Cycle
题意:有一个 \(N\) 个点的有向图,节点标号为 \(0 \sim (N - 1)\)。这张图初始时只有 \(N - 1\) 条边,每条边从 \(i\) 指向 \(i + 1\),边权为 \(0\)。
对于每一对 \(i, j\)(\(0 \le i, j \le N - 1\),\(i \ne j\)),Snuke 会加入新边 \(i \to j\),如果 \(i < j\) 则边权为 \(-1\),否则边权为 \(1\)。
Ringo 不喜欢图中的负环,所以他想要删掉一些 Snuke 加入的边,使得最终得到的图没有负环。但是删掉每一条边是有代价的,具体地说,删掉 \(i \to j\) 这条边,要花费 \(A_{i, j}\) 的代价。
请问满足图中不存在负环的最小删边代价是多少?
思路:考虑按照差分约束连边,这样设最短路为 \(dis_i\),那么设 \(d_i=dis_{i+1}-dis_i\),这样 \(dis_i\) 是单减的,而且一条 -1 边合法就对应着一段 \(d_i\) 的和大于 0,一条 1 边同理,这样我们考虑对合法的 \(d\) 序列来求答案。
我们发现,\(d_i\geqslant 0\),而且如果 \(d_i>1\) 的话说明凭空多产生了一条边,可以减掉,这样 \(d_i\in\{0,1\}\),于是就可以DP,设 \(f_{i,j,k}\) 表示在 \(i\),当前连续段长度为 \(j\),后一个连续段长度为 \(k\) 的最小代价,这样就有转移:
\(f_{i,j,k}=f_{i-1,j-1,k}+\sum\limits_{l\leqslant i-j-k}A_{i,l}+\sum\limits_{i-j+1\leqslant l\leqslant i-1}A_{l,i}\)
如果 \(j=1\),那么有 \(f_{i,j,k}=\min\limits_l\{f_{i-1,k,l}+\sum\limits_{p\leqslant i-k+1}A_{i,p}\}\)。
trick:通过模型转换得出合法条件,再进行 DP。
[ABC077D] Small Multiple
题意:给定一个整数 \(K\)。求一个 \(K\) 的正整数倍 \(S\),使得 \(S\) 的数位累加和最小。
思路:先考虑 DP,设 \(f[i][S]\) 表示长为 \(i\) 的数模 K 余 S 时的最小数位和,那么转移有 \(f[i][S]\leftarrow f[i+1][(10S)\pmod K],f[i][S]+1\leftarrow f[i][S+1]\)。
我们发现问题在于我们不知道位数的大小,这就导致转移很困难。但是其实我们并不需要知道具体的位数,于是可以去掉第一维。但是这时转移并不是一个 DAG,不能直接 DP,而这个转移有很像最短路,于是可以考虑用最短路求解。由于边权只有 01,于是可以直接用 01bfs,复杂度 \(O(n)\)。
trick:如果 DP 转移不能直接递推,可以尝试通过转移的性质用其他方法(如最短路)进行转移。
CF1372E Omkar and Last Floor
题意:一个 \(n\times m\) 的矩阵,初始值全为 \(0\) ,每一行都被分成若干部分,每一部分只能出现一个 \(1\) ,设 \(q_i\) 为第 \(i\) 列 \(1\) 的个数,求 \(\sum_{i=1}^mq_i^2\) 的最大值。
思路:首先,可以贪心地想到让尽量多的 \(1\) 都在同一列,然后考虑区间 DP(可能是因本身就与区间相关,而且一个区间去掉这一列剩下两个子区间),预处理出\(s_{l,r,k}\)表示\(\sum\limits_{[l',r']\in [l,r]}[k\in [l',r']]\),于是有\(dp[l][r]=\min(dp[l][k-1]+dp[k+1][r]+s_{k,l,r}^2)\)。
trick:通过贪心确定了一列的答案,把问题变成了两个区间的问题,于是考虑用区间 DP 来求解。
CF1420E Battle Lemmings
题意:有 \(n\) 个守卫排成一行,编号为 \(1\) 到 \(n\)。这些守卫里有的拿着盾牌,有的没有。一个守卫只能同时拿一个盾牌。
称一对守卫是被保护的,当且仅当这两个守卫都没拿盾牌,但他们之间有守卫拿了盾牌。
每一秒,指挥官可以下达两种命令中的一种,分别是:
- 选一个带盾牌的守卫,把它的盾牌给他左边的守卫
- 选一个带盾牌的守卫,把它的盾牌给他右边的守卫
请求出时刻 \(0\) 到 \(\frac{n \times (n - 1)}{2}\) 中每个时刻最多有多少对守卫是被保护的。
思路:首先,被保护的对数等于总对数减去每一段连续的 0 的对数。同时,如果我们知道了初始态和最终态,可以贪心地求出最少的交换次数。
设 \(dp[i][x][y]\) 表示在第 \(i\) 为填 1,当前已经操作了 \(x\) 步,前 \(i\) 位里一共有 \(y\) 个 \(1\) 的情况下未被保护的对数的最小值,转移时枚举下一个 \(1\) 填在哪里,就可以做到 \(O(n^5)\)。
trick:转化贡献使得更好计算。
CF1422E Minlexes
题意:给定一只由小写字母组成的字符串 \(s\),可以先删除任意个 两相邻相同字符,再把剩下的部分拼接起来。对于 \(s\) 的每个后缀,求进行上述操作后得到的字典序最小的字符串。
思路:首先可以直接 DP,设 \(f_i\) 表示i这个后缀的答案,转移有两种,一种是 \(s_i\ne s_{i+1}\),那么就直接继承,把 \(s_i\) 放在最前面,一种是 \(f_i=\min(f_{i+2},s_i+s_i+f_{i+2})\)。为了保证比较的复杂度,可以记下最前面的不相同的相邻两位的大小关系,这样比较就比较简单。
在写法上,可以用结构体来减小代码难度。
trick:DP 可以维护的并不仅仅是数值,只要是可以比较的方便维护的信息也可以,要灵活处理。
CF1572C Paint
题意:给定长度为 \(n\) 的颜色序列 \(a_i\),每次你可以选择任意长度的连续且颜色相同的一段位置,将其全部变成任意同一种颜色,问你最少总共需要多少次操作才能使得整个序列颜色相同。保证每一种颜色初始时在序列中最多只有20个位置(是该种颜色)。
思路:直接区间 DP,设 \(dp[i][j]\) 表示把 \(i\sim j\) 涂成和 \(j\) 一样的颜色的最小代价,那么转移有 \(dp[i][j-1]\rightarrow dp[i][j],dp[i+1][j]\rightarrow dp[i][j]\) 或者找到最近的 \(c_k=c_j\),有 \(dp[i][k]+dp[k+1][j]\)。于是就做完了。
trick:可以通过添加额外限制简化 DP 的状态。
CF1580B Mathematics Curriculum
题意:求长为 \(n\) 的排列且恰好有 \(k\) 个数满足包含这个数的区间中不同的最大值个数恰有 \(m\) 个。
思路:考虑上笛卡尔树,那么就变成了求恰好有 \(k\) 个点的深度为 \(m\) 的笛卡尔树个数。
于是直接设 \(f[i][j][k]\) 表示大小为 \(i\) 的子树中,有 \(k\) 个点层数为 \(m\),且子树根节点的深度是 \(j\) 的方案数。转移有 \(f[i][j][k]=\sum\limits_a\sum\limits_b\binom{i-1}{a}\times f[a][j+1][b]\times f[i-1-a][j+1][k-b-[j==m]]\)。
trick:区间最大值有关 \(\rightarrow\) 笛卡尔树。
CF1854E Boxes and Balls
题意:给定一个长度为 \(n\),由 \(0,1\) 构成的数组 \(a\)。你可以对他进行以下操作:
- 交换数列中一对相邻的 \(0\) 和 \(1\)。
问在恰好 \(k\) 次操作后,可以产生多少种本质不同的数组。
思路:首先,我们的策略肯定是先花最小的代价移动到目标状态,然后再选一对01反复横跳,因此只要步数和 \(k\) 的奇偶性相同就行。
设 \(dp[i][j][k]\) 表示前 \(i\) 个位置,放了 \(j\) 个球,总移动次数为 \(k\) 的方案数,那么转移有 \(dp[i][j][k]\rightarrow dp[i+1][j][k],dp[i][j][k]\rightarrow dp[i+1][j+1][k+|i+1-a_j|]\),这样的复杂度是 \(O(n^2)\) 的,不够优秀。
设 \(z(b,i)\) 表示方案 \(b\) 相较于一开始,在 \(i\) 前缀多出的球的个数,那么会有 \(|z(b,i)|\) 个球通过第 \(i\) 个间隔,而相邻的 \(z(b,i)\) 相差不会超过 1,因此 \(\max(z(b,i))\) 在 \(O(\sqrt k)\) 量级。
于是就设 \(dp[i][j][k]\) 表示考虑前 \(i\) 个位置,\(z(b,i)=j\) 且 \(\sum|z(b,i)|=k\) 的方案数,这样就是 \(O(n^2\sqrt n)\) 的了。
trick:对于不降序列对应位置的距离之和,可以在每个间隔处统计答案。
P8258 [CTS2022] 独立集问题
思路:抽象 DP 题。
首先,可以把操作当成是把周围的点的权值吸过来,而且操作多次自己可以就相当于是取相反数。
于是就可以设计状态: \(dp[i][0/1/2][0/1]\) 表示当前考虑到点 \(i\) ,自己被父亲吸走/被儿子吸走/把别人吸走,是否吸了父亲的权值的最优答案,那么转移时先求出 \(A_j\) 表示 \(j\) 的最优答案,\(B_j\) 表示 \(j\) 不吸走 \(i\) 的最优答案,\(C_j\) 表示 \(j\) 被 \(i\) 吸走的最优答案,这样就好转移了。
P3953 [NOIP2017 提高组] 逛公园
题意:求有多少条从 1 到 \(n\) 的路径满足比最短路长不超过 K。
思路:容易想到设 \(f[x][k]\) 表示 \(dis(x,n)\leqslant mindis(x,n)+K\) 的方案数,然后对于一条边 \((u,v,w)\) ,有 \(f[u][k]=\sum f[v][k-(mindis(v,n)-mindis(u,n)+w)]\),然后记忆化搜索即可。
trick:对于图上问题,不好确定 DP 转移顺序可以用记忆化搜索。
P4649 [IOI2007] training 训练路径
题意:给定一个无向图,你需要删掉一些边,使得此图没有长度为偶数的简单环。删掉第 \(i\) 条边有 \(C_i\) 的花费,有些边又是不能删的。不能删的边形成图的一棵生成树。保证每个点的度数不超过 10。
思路:没看出最终是个仙人掌,输麻了。
首先,把会形成偶环的非树边删掉,这时只剩一些奇环,我们发现,如果有一条边同时出现在两个奇环里,那么就会形成一个偶环,因此每条边至多出现在一个环里,即这张图是个仙人掌。
于是就可以 DP。先变成求可以留下的非树边权值的最大值。我们设 \(f[i][S]\) 表示点 \(i\) 不考虑 \(S\) 中的儿子的答案,那么对于一条 \(lca(u,v)=i\) 的非树边,如果不选,那么不选时有 \(f[i][S]=\sum\limits_{son\notin S}f[son][0]\),选的时候贡献有两部分,一是 \(f[u][0]+f[v][0]\),二是对于路径上其他点 \(p\),设 \(p\) 不包含 \(u\) 或 \(v\) 的儿子集合为 \(K\),贡献为 \(\sum f[p][K]\),于是有 \(f[i][S]=\max(f[i][S|x|y]+val)\)。
trick:通过特殊的数据范围可以往状压的方面想。
P5299 [PKUWC2018] Slay the Spire
题意:有 \(n\) 张强化牌和 \(n\) 张攻击牌,每张牌有一个权值(强化牌的权值大于 1),每张强化牌能使所有攻击牌的权值乘上这张强化牌的权值,每张攻击牌造成的伤害等于这张攻击牌的权值。现在,以等概率抽出 \(m\) 张牌,并以最优策略使用其中至多 \(k\) 张牌造成最大的伤害。求所有情况下,造成伤害总和。
思路:好像就是组合数+推式子?
首先确定策略。通过调整法可以证明,在至少有一张攻击牌的情况下,选择强化牌会更优。
设 \(f[i][j]\) 表示在前 \(i\) 张强化牌中选择 \(j\) 张且第 \(i\) 张被选中时所有强化牌的乘积之和,\(g[i][j]\) 表示攻击牌的和,\(fs[i][j]\) 表示 \(\sum\limits_{k=1}^if[k][j]\),\(gs[i][j]\) 同理,于是有 \(f[i][j]=a_ifs[i-1][j-1],g[i][j]=b_i\binom{j-1}{i-1}+gs[i-1][j-1]\)。
然后分两种情况。
1.强化牌数量小于 \(k-1\),那么一定是选所有的强化牌和最大的攻击牌,于是枚举最后选择的强化牌数量 \(i\) 和最后选择的攻击牌 \(j\),那么强化牌的贡献是 \(fs[n][i]\),攻击牌的贡献是 \(g[j][k-i]\),这时剩下 \(m-k\) 张的方案数是 \(\binom{m-k}{n-j}\)。
2.强化牌数量大于等于 \(k-1\),那么肯定是选权值最大的 \(k-1\) 张强化牌和最大的攻击牌,枚举 \(i,j\) 为最后选择的强化牌、攻击牌,那么强化牌的贡献是 \(f[i][k-1]\),攻击牌的贡献是 \(b_j\),这时剩下的 \(m-k\) 张牌方案数就是 \(\binom{m-k}{2n-i-j}\)。
trick:从边界情况入手。
P9330 [JOISC 2023 Day1] Festivals in JOI Kingdom 2
思路:神仙DP题。
首先,发现正着求不好求,于是考虑求假贪心可以求出正确答案的方案数。
我们把所有区间画出来,红色的是正贪心的区间,蓝色的是假贪心的区间,紫色是正贪心和假贪心一样的区间,剩下的是黑区间。然后就可以发现一些性质:
对于红区间,首先是无交,并且相邻两个红区间的右端点之间和第一个红区间之前没有完整区间。
对于蓝区间,首先是无交,并且前一个蓝区间的右端点和后一个蓝区间的左端之间以及第一个蓝区间之间不存在其他的左端点。
对于一对红蓝区间,红区间的右端点一定大于前一对里蓝区间的右端点,不大于这一对里蓝区间的右端点。
于是就可以DP了。考虑顺次加入一对红蓝区间,那么就需要枚举新加进来的黑区间,同时,因为黑区间的左端点要满足不在任意两个蓝区间之间,还需枚举可以作为黑区间左端点的数量,这样就是2D/1D的。
转移显然没法优化,考虑优化状态。我们发现,如果倒着加入红蓝区间,那么对黑区间的限制只有右端点,这样就是1D/1D的。
正解需要把转移写成卷积,然后用任意模数NTT或者 \(O(n^{\log^3_2})\) 的 \(Karatsuba\) 算法,可惜我都不会,不过 \(O(n^2)\) 可以冲过去。
P9338 [JOISC 2023 Day3] Chorus
思路:考场上推出了一些性质,但是还是只会指数级暴力。
这种题有一种比较通用的想法,把 A/B 当做向上、右走,这样可以转成平面,然后 AB 翻转相当于把一个角翻折,这样最终 \(k\) 个子序列对应有 \(k\) 个拐弯。
发现恰好这个限制很可以 WQS 二分,于是就 WQS 二分。设 \(w(l,r)\) 对应从 \((l,l)\) 到 \((r,r)\) 的代价,然后设 \(t_i\) 表示第 \(i\) 向上前向右的次数,\(pre_i\) 表示 \(\sum t_i\),\(cnt_i\) 表示 \(t_j\leqslant i\) 的 \(j\) 的个数,\(sum_i\) 表示 \(t_j\) 的和,那么 \(w(l,r)=(r-1)(cnt_{r-1}-l+1)-sum_{r-1}+pre_{l-1}\),于是直接斜率优化即可。
trick:只有两种元素 \(\rightarrow\) 转成平面上的路径进行处理;恰好 \(\rightarrow\) WQS 二分。
P9339 [JOISC 2023 Day3] Cookies
题意:有 \(n\) 个饼干,每种 \(a_i\) 个,要装进盒子里,每个盒子里的饼干种类不能相同,而且每个盒子的大小必须为给定的集合 \(b\) 中的数,求最少的盒子数量。
思路:首先确定是否有解。可以顺推。对于最大的桶 \(c_1\),至少要有 \(c_1\) 种不同的饼干;对于次大的桶 \(c_2\),需要在装满 \(c_1\) 个后至少要有 \(c_2\) 个不同的饼干...因此可以推出结论:
于是就知道了取前若干个桶时大小的上界,于是就可以 DP,设 \(dp[i][j][k]\) 表示在前 \(i\) 大的桶中取 \(j\) 个能否和为 \(k\),发现最后一位可以用 bitset 来存,而且第二维有用只有 \(O(n\ln n)\) 种,于是就可以做到 \(O(\dfrac{n^2\ln n}{w})\)。
trick:分析实际有效的数据范围;如果 DP 维护的值只有 01 可以考虑用 bitset。
[ARC121F] Logical Operations on Tree
题意:给定一棵树,给每个点填 \(0\) 或 \(1\),给每条边填 \(\text{AND}\) 或 \(\text{OR}\),在所有 \(2^{n+n-1}\) 种填法中,计数有多少种满足存在一种缩边的顺序,使得每次把一条边的两个端点缩成一个点,权为原端点与边的运算值,最终点的权为 \(1\)。
思路:首先,策略肯定是先把 AND 的边缩起来,因此合法的条件就是有一个 1 点周围都是 OR 边或者一个 AND 边连通块内都是 1 点。发现这个条件还是比较复杂,考虑补集,就是每个 AND 连通块内至少有一个 0 点。于是就可以 DP。对于一条边,如果当做 OR 边就相当于是把连通块割开。
设 \(dp[i][0/1]\) 表示当前考虑到点 \(i\),当前连通块是否合法的方案数,那么有转移两种:
-
\(dp[i][0]\leftarrow 2dp[i][0]\times dp[j][0]+dp[i][1]\times dp[j][0]+dp[i][0]\times dp[j][1]\)
-
\(dp[i][1]\leftarrow dp[i][1]\times dp[j][1]\)
trick:正难则反。
CF1348E Phoenix and Berries
题意:小P在花园里摘红梅和蓝莓。
他一共有 \(n\) 棵树,第 \(i\) 棵树上有 \(a_i\) 个红梅和\(b_i\)个蓝莓。由于小P有强迫症,所以他希望一个篮子里的梅子都来自同一棵树或都是一种颜色的(或两者同时满足)。
给出篮子的最大容量 \(k\),求能够被装满的篮子数量的最大值(不必用完所有梅子)。
思路:大概的方向是对的,不过应该是还差一步。
有一个结论,每个树至多只会装满一个红、蓝都有的篮子。
考虑DP。设 \(f[i][j]\) 表示当前在第 \(i\) 棵树,目前剩下 \(j\) 个没有被装进篮子里的红梅的最大答案。转移时枚举当前树上有多少个红梅和蓝莓放同一个袋子里。
CF1539E Game with Cards
题意:有两个数 \(a\) 和 \(b\) ,初始每个数都是 \(0\) 。你需要进行 \(n\) 次操作,并且数据保证所有操作所涉及的数值不超过 \(m\) 。
每次操作会有三行。对于第 \(i\) 次操作:
第一行是一个数 \(k_i(0\le k_i\le m)\) ,你需要用它去替换 \(a,b\) 中的任意一个,且仅一个。
第二行是两个数 \(a_{l,i}\) 和 \(b_{l,i}\) \((0\le a_{l,i}\le b_{l,i}\le m)\) ,需要满足操作后 \(a_{l,i}\le a\le b_{l,i}\) 。
第三行是两个数 \(a_{r,i}\) 和 \(b_{r,i}\) \((0\le a_{r,i}\le b_{r,i}\le m)\) ,需要满足操作后 \(a_{r,i}\le b\le b_{r,i}\) 。
问是否可以完成所有的 \(n\) 次操作。若可以输出 "Yes",并在下一行依次输出每次操作是改的哪一个数, \(0\) 表示改的第一个数, \(1\) 表示改的第二个数。若不能则输出 "No"。有多种方案任意输出一种即可。
思路:很有意思的题目。
自己只想到了类似二分图匹配的做法,不太能过的样子。
正解是很巧妙的 DP。
首先有一个常见的 \(trick\) 是把答案序列看成一段 0/1 交替,那么对于一段为 0 的区间 \([l,r]\),要满足:
-
\(\forall i\in[l,r],a_i\in[L0_i,R0_i]\)
-
\(a_{l-1}\in[L1_i,R1_i]\)
于是就可以 dp。设 \(dp[i][0/1]\) 表示第 \(i\) 位的答案为0/1是否合法,以 \(0\) 为例,那么转移时如果存在一个为 \(dp[j][1]\) 为 \(1\) 的位置 \(j\) ,那么如果 \([j+1,i]\) 这一段是合法的, \(dp[i][0]\) 就可以为 \(1\) 。于是可以预处理出每个位置最远的合法左端点,然后就可以简单转移了,复杂度 \(O(n\log n)\)。
但是这还不够。我们发现,对于每个 \(i\),我们只需要知道满足 \(dp[j][1]=1\) 的最大的 \(j\),这就意味着我们只需要知道最优的 \(j\),于是考虑倒着转移,实时维护当前最小的满足 \(dp[j][1]=1\) 的最小的 \(j\),这时只需考虑当前这一段是否合法就行了。复杂度 \(O(n)\)。
trick:对于 01 序列,可以把连续段一起考虑,这样就只剩下 01010 的结构。
CF1612F Armor and Weapons
思路:想到了比较关键的部分(好像对于其他人都是“显然”?)
关键部分:答案的上界近似是 \(O(\log n+\dfrac{m}{n})\),证明应该还比较显然。
于是这题就可以很暴力的做了。设 \(dp[i][j]\) 表示在第 \(i\) 步时,盔甲为 \(j\) 时武器的最大值,\(f[i][j]=0/1\) 表示列表中是否有 \((i,j)\),那么转移有
把第一维滚掉暴力做就可以了。
trick:分析实际数据范围,减小状态量。
P4895 独钓寒江雪
题意:给定一棵无根树,求其中本质不同的独立集的个数。
思路:好题。
因为是无根树,而且和同构有关,不难想到以重心为根转成有根树。
考虑和正常的求独立集的 DP 不同在哪里,就是如果有同构的子树那么方案数会有重复,于是可以把同构的子树一起处理,可以用组合数计算。
对于两个重心的情况,新建一个节点当做根即可。
trick:无根树同构 \(\rightarrow\) 有根树。
P5243 [USACO19FEB] Moorio Kart P
题意:\(K\) 个连通块的森林,边有边权。对于所有加入 \(K\) 条长 \(X\) 的边使图变成一个基环树,且环经过所有加入的边,且环的长度 \(\geq Y\) 的方案,求环的长度的和。
思路:没看懂 \(O(nY)\) 的做法,于是只能写 \(O(nY\sqrt{Y})\) 的做法。
复杂度是因为 \(\sum\min(n_i^2,Y)\) 是 \(O(Y\sqrt{Y})\) 的。
trick:复杂度的精细分析。
P5853 [USACO19DEC] Tree Depth P
题意:逆序对数为 \(K\) 的排列的笛卡尔树的深度和。
思路:好题。
刚开始看时还找了一下关于笛卡尔树深度计数的博客,好像没啥,只有 cmd 有博客,不过没太看懂。
有 \(O(n^3)\) 的神秘做法。
首先,考虑怎么求长为 \(n\) 的有 \(m\) 个逆序对的排列,可以设 \(f[i][j]\) 表示长为 \(i\) 的有 \(j\) 个逆序对的排列个数,这就是个背包,可以用前缀和优化到 \(O(n^3)\)。
回到这道题,我们求深度和,可以考虑求对深度有贡献的点对数,即有多少对 \((u,v)\) 满足 \(v\) 是 \(u\) 的祖先,这个条件等价与 \(a_v<a_u\) 且 \(u,v\) 间没有比 \(a_v\) 小的数。
于是考虑先固定 \((u,v)\) 内的数,再确定 \(u,v\),最后固定外面的数。除 \(v\) 以外的数可以随便填,对于 \(v\) ,如果 \(v\) 在 \(u\) 后面,那么会对逆序对产生 \(v-u\) 的贡献。
于是求出 \(f\),然后枚举 \(v-u\),在背包中撤销 \(v-u\),此时 \(f[n-1][K-max(0,v-u)]\) 就是答案。
trick:撤销背包。
P6596 How Many of Them
题意:求有 \(n\) 个点有标号且割边不超过 \(m\) 条的无向图个数。
思路:有点小巨大的数学题。
首先有前置问题,求 \(n\) 个点的无向连通图数量,这个有朴素的 \(O(n^2)\) 的 DP,即设 \(f_i\) 表示 \(i\) 的无向连通图个数,那么容斥一下,有 \(f_i=2^{\frac{i(i-1)}{2}}-\sum\limits_{j=1}^{i-1}f_j\times C_{i-1}^{j-1}\times2^{\frac{(i-j)(i-j-1)}{2}}\)。
然后就是求有 \(i\) 个割边的方案数。我们设 \(f[i][j]\) 表示 \(i\) 个点有 \(j\) 个割边的连通图个数,同样是以 1 号点所在的双连通块为基准来计算。我们设 \(g[i][j][k]\) 表示有 \(i\) 个点 \(j\) 个连通块 \(k\) 个割边的图个数,那么当 \(j>0\) 的时候,有 \(f[i][j]=\sum\limits_{k=1}^{i-1}f[k][0]\times C_{i-1}^{k-1}\times\sum\limits_{x=1}^{min(i-k,j)}g[i-k][x][j-x]\times k^x\),其中 \(k\) 是 1 所在连通块大小,\(x\) 是剩余连通块个数。当 \(j=0\) 的时候,\(f[i][0]=h_i-\sum\limits_{j=1}^{i-1}f[i][j]\)。
最后是 \(g\) 的求法。枚举标号最小的点所在的连通块的大小和割边数量,从这个连通块向 1 所在连通块连边,有 \(g[i][j][k]=\sum\limits_{x=1}^{i}\sum\limits_{y=0}^{k}f[x][y]\times C_{i-1}^{x-1}\times x\times g[i-x][j-1][k-y]\)。
trick:与图计数的问题通常可以枚举 1 号点所在联通块满足什么条件进行转移。
P8275 [USACO22OPEN] 262144 Revisited P
题意:定义一个序列的权值为:每次操作可以选择相邻的两个数并替换为两数最大值 +1,求一个序列的所有子区间的权值之和。
思路:抽象题。
首先是 \(O(nV)\) 做法。
设 \(cnt_v\) 表示答案 \(\ge v\) 的区间数量,那么答案为 \(\sum cnt_v\)。
记 \(f_l\) 表示最大的满足 \([l,r)\) 答案 \(<v\) 的 \(r\),那么 \(cnt_v=C^2_{n+1}-\sum(f_l-l)\)。
当 \(v\rightarrow v+1\) 时,有转移
\(f'_l=\begin{cases} l+1&a_l=v\\ f_{f_l}&a_l\ne v\\ \end{cases}\)
接着有结论:所有答案为 \(v\) 的极长区间数之和为 \(O(n\log n)\)。
记 \(cnt_n\) 长为 \(n\) 时的答案,可以证明 \(cnt_n\le O(\log n!)\le O(n\log n)\)
证明:设 \(p\) 为最大值所在下标,则 \(cnt_n=cnt_{p-1}+cnt_{n-p}+\) 包含 \(p\) 的极长区间数,又有对于一个左端点 \(l\),\([l,n]\) 的答案 \(\le+[l,p]\) 的答案 \(+O(\log\dfrac{n}{p})\),因此至多有 \(O(\log\dfrac{n}{p})\) 个极长区间,于是对于 \(O(p)\) 个左端点,包含 \(p\) 的极长区间个数 \(\le O(p\log\dfrac{n}{p})\le O(\sum\limits_{i=0}^{p-1}\log\dfrac{n-i}{p-i})=O(\log\dfrac{n!}{p!(n-p)!})\),于是结论成立。
接着有两个优化:
优化 1:只维护所有极长的区间 \([l,f_l]\),条件是 \(\max(l,f_{l-1})<f_l\)。
此时有两种转移,\([l,f_l)\rightarrow[l,f'_l)\),以及对于 \(a_l=v\) 的位置会新增 \([l,l+1)\)。
优化 2:若 \(\min(a_{l-1},a_{f_l})\ge v\),那么 \(f_l\) 不变,可以在 \(l,f_l\) 处记录。
这样复杂度就是 \(O(n\log n)\) 的了。
trick:分析合法情况的数量。
[AGC032D] Rotation Sort
题意:给定一个排列,你可以花费 \(A\) 使一个区间最左边的数跑到最右边,或者花费 \(B\) 的代价使最右边到最左边, 求把整个序列变成升序的最少花费。
思路:自己口胡了一个类区间DP的东西。
正解:首先,每个数至多被移动一次,于是就只有被移动过的和未被移动过的两种,那么对于两个相邻的被固定了的数,他们中间的数的贡献就是确定的,然后就可以设计 \(f[i]\) 表示固定了 \(p_i\) 时的答案,\(O(n)\) 转移就可以做到 \(O(n^2)\)。
trick:找性质来优化状态。
[AGC055D] ABC Ultimatum
题意:称一个长度为 \(3n\),只由 \(\text{A,B,C}\) 组成的字符串是好的,当且仅当其能划分成 \(n\) 个长度为 \(3\) 的子序列,每个子序列都是 \(\text{ABC}\),或者 \(\text{BCA}\),或者 \(\text{CAB}\)。求把问号替换成 \(\text{A,B,C}\) 使得字符串是好的的方案数。
思路:对于判定是否合法有两种方向:DFA 和找充要条件。
于是就来找充要条件。
对于任意一个前缀,假设我们只用 BCA,CAB,那么 A 的数量一定不能超过 C 的数量,可是又有了 ABC ,于是就可以得出 A 的数量减去 C 的数量不超过 ABC 的数量。
于是大胆猜想,设 \(cnt_A(i)\) 表示 \(1\sim i\) 中 A 的数量,设 \(MA=\max cnt_A(i)\),\(MB,MC\) 同理,那么有结论:\(MA+MB+MC\le n\)。
必要性是显然的,因为 \(ABC+BCA+ACB=n\)。
接着就是充分性。对于第 \(i\) 个 A,我们选择第 \(i+MB\) 个 B 和第 \(i+MB+MC\) 个 C (对 \(n\) 取模),这时可以发现 B 一定在 A 后面和 C 前面,而 C 又一定不会在下一次的 A 后面,那么我们选择的循环顺序就一定是 ABC。
于是就可以直接 DP,设 \(f[i][a][b][c][MA][MB][MC]\) 来表示状态,因为 \(a+b+c=i\),我们可以去掉一维,于是就是 \(O(n^6)\) 的了。
trick:对于判定是否合法有两种方向:DFA 和找充要条件。
CF713E Sonya Partymaker
题意:一个长度为 \(m\) 的环,最初有 \(n\) 个点上有人。每秒可以让所有人都走一步(可以选择每个人的方向,但中途不能变换方向)。问使所有点都被走到的最短时间。
思路:算是 Lanterns 的加强(?)版。
考虑二分答案,于是就变成了每个点覆盖范围一样的 lanterns。
先不考虑是环的情况,那么可以直接 DP,设 \(dp[i]\) 表示 \(1\sim i\) 最多能覆盖多远,转移有:
- \(dp[i-1]\ge pos[i]-1\),\(dp[i]=pos[i]+x\)。
- \(dp[i-1]\ge pos[i]-x-1\),\(dp[i]=pos[i]\)。
- \(dp[i-2]\ge pos[i]-x-1\),可以让 \(i\) 代替 \(i-1\) 往回,\(dp[i]=pos[i-1]+x\)。
然后考虑环上的情况。我们发现,如果选择两个距离最大的点断开就不会有影响。
再考虑 1 号点的情况。
- 向左走,那么 \(dp[1]=0\),要求 \(dp[n]\ge m-x-1\)。
- 向右走且没走到 2,那么 \(dp[2]=b[2]\),要求 \(dp[x]\ge m-1\)。
- 向右走且走到 2,那么让 2 往回走显然更优,于是 \(dp[2]=x\),要求 \(dp[n]\ge m+pos[2]-x-1\)。
trick:覆盖全集 \(\rightarrow\) 覆盖前缀。
CF1149B Three Religions
题意:给定长度为 \(n\) 的母串和三个子串 \(s_1,s_2,s_3\)。初始时子串均为空。有 \(q\) 次询问。你需要支持两种操作:向某个子串末尾添加一个字母,或者删去某个子串末尾的字母。在每次操作后,你需要回答,是否能从母串中分离出三个不相交的子序列(不改变字符原有顺序),满足这三个子序列恰好是 \(s_1,s_2,s_3\)。
思路:状态基于 \(n\) 的 DP 是好想的,但是修改困难,于是想办法做到状态不基于 \(n\)。
因为只用判断合法,我们可以设 \(f[a][b][c]\) 表示 \(s_1,s_2,s_3\) 分别匹配了 \(a,b,c\) 位时最短在母串的第几位,这样复杂度是 \(O(n^3)\) 的,而且一次修改是 \(O(n^2)\) 的,最终判断 \(f[|s_1|][|s_2|][|s_3|]\) 是否为 \(\text{inf}\) 即可。
trick : 判是否可行可以把一般状态的一维去掉,然后对剩余状态求出这一维的最小/最大值,再进行判断。
CF1528E Mashtali and Hagh Trees
题意:一棵有向树称为 \(Hagh\) 树当且仅当以下条件满足:
1.最长有向路径长度为 \(n\)。
2.每个点的入度+出度 \(\le 3\)。
3.如果 \(u\) 是 \(v\) 的祖先,或 \(v\) 是 \(u\) 的祖先,则 \(u,v\) 是朋友。对于树上任意两个结点 \(x,y\),如果 \(x,y\) 不是朋友,则他们必须要有一个公共朋友。
给出 \(n(1\le n\le 1e6)\),求有多少棵不同构的 \(Hagh\) 树。
思路:大分讨题。
自己想到了几乎全部的做法,只不过不想分讨。
首先,发现最终树的形态是一个点为根,然后有不超过 3 棵子树,每棵子树都是根向/叶向树,然后最长有向链长度是 \(n\)。于是我们求出深度为 \(x\) 的叶向/根向树的方案数,然后拼起来,分讨一下即可。
P3336 [ZJOI2013] 话旧
思路:大分讨 DP。
设 \(f[i][0/1]\) 表示到 \((x_i,y_i)\) 时前一段是 \(1/-1\) 的方案数,\(maxn[i][0/1]\) 是最大值。
- \(x_i-x_{i-1}=y_i-y_{i-1}\),只能是 \(k=1\) 的线段;
- \(x_i-x_{i-1}=y_{i-1}-y_i\),只能是 \(k=-1\) 的线段;
- 设 \(len=x_i-x_{i-1}-y_i-y_{i-1}\),可以根据这个推出锯齿形的方案数。
关于最大值,有些情况不合法,设成 \(-inf\) 即可。
CF441E Valera and Number
题意:给出一个数 \(x\),对它进行 \(k\) 次操作,每次操作:
- 以 \(p \%\) 的概率对它乘以 \(2\)。
- 以 \((100 - p) \%\) 的概率对它加上 \(1\)。
求该数最终二进制下末尾 \(0\) 个数的期望。
题意:发现 +1 操作可能造成进位,不好处理,于是考虑时间倒流,这样不会影响最低位的 0。
我们把末尾的操作看成 \(j\) 个 \(+1\) 后 \(k\) 个 \(\times 2\),
那么如果是 \(+1\),就令 \(j\rightarrow j+1\)。
否则就是 \(\times 2\),如果 \(j\) 是偶数,就有 \(j\rightarrow j/2\),\(k\rightarrow k+1\);如果 \(j\) 是奇数,那么最低位的 1 已经确定了,后面的操作也不会有影响,可以直接贡献到答案。
这样就可以做到 \(O(n^2)\)。
trick:费用提前计算。
P3600 随机数生成器
题意:给一个序列 \(a\),\(a\) 中每个数是在 \([1,x]\) 中的随机数,然后给定 \(q\) 个询问 \(\min\limits_{j=l_i}^{r_i}a_j\),求 \(q\) 个询问的最大值的期望。
思路:首先,如果一个区间完全包含了另一个区间,那它的最小值一定不大于它包含的区间的最小值,那么它就不做贡献。
如果直接求期望没什么好方法,我们考虑把它转化为一个计数问题,最后除以 \(x^n\) 就是答案。
设 \(h_i\) 为最终答案 \(\leqslant i\) 的方案数,然后最终答案为 \(i\) 的方案数就是 \(h_i-h_{i-1}\),这等价与每个区间的最小值都 \(\leqslant i\),即每个区间都存在 \(\leqslant i\) 的数,我们设 \(g_j\) 表示在 \([1,n]\) 内选出 \(j\) 个点,使得每个区间都被覆盖的方案数,然后 \(h_i\) 就等于 \(g_j\times i^j\times (x-i)^{n-j}\)。
然后考虑用 DP 算出 \(g_j\)。我们设 \(f[i][j]\) 表示前 \(i\) 个位置放了 \(j\) 个点,且第 \(i\) 个位置必须放点,覆盖了所有左端点 \(\leqslant i\) 的区间的方案数。先预处理出 \(fl[i]\) 和 \(fr[i]\) 分别表示覆盖了 \(i\) 的最左/右的区间,如果没有就是左侧最接近它的区间。因此 \(f[i][j]=\sum\limits_{fr[k]+1>=fl[i]}f[k][j-1]\),容易发现 \(k\) 处在一个区间内,因此可以前缀和优化。然后就得到 \(g_j=\sum\limits_{fr[i]=q}f[i][j]\),然后也可以算出 \(h_i\)。
复杂度\(O(n^2)\)。
trick:期望题目不仅可以用期望的线性性,还可以直接转成计数。
CF115D Unambiguous Arithmetic Expression
题意:给一个不含括号的表达式,求有多少种方法给其加上括号使得其仍合法。
思路:好像区间 DP 减减枝可以过,但我没试。
设 \(dp[i][j][0/1]\) 表示考虑到第 \(i\) 个位置,前面已经有 \(j\) 个左括号为匹配,当前填的是"("还是")"。
然后考虑转移,方向是 \(dp[i][j][0/1]\rightarrow dp\cdots\)。
先考虑 \(dp[i][j][1]\),那么有 \(dp[i][j][1]\rightarrow dp[i+1][j+1][0]\)(在后面加一个左括号就是\(j+1\)个左括号未匹配),\(dp[i][j][1]\rightarrow dp[i][j-1][1]\)(在自己这里再添一个右括号)。
然后是 \(dp[i][j][0]\),那么有
复杂度 \(O(n^2)\)。
trick:遇到合法括号串相关,可以把当前匹配完还剩多少左括号计入状态。
P2612 [ZJOI2012]波浪
题意:定义一个排列的波动值为相邻两数之差的绝对值之和,求所有长为 \(n\) 的排列波浪值不小于 \(m\) 的概率,保留小数点后 \(k\) 位。
思路:看到绝对值不好处理,一个常见的套路是考虑从小到大插入每个数依次计算贡献。我们发现一个数 \(i\) 作的贡献只和前 \(i-1\) 个数组成的连续段情况以及和边界的关系有关。我们设 \(dp[i][j][k][l]\) 表示考虑到第 \(i\) 个数,一共组成了 \(j\) 个连续段,总贡献为 \(k\),有 \(l\) 个连续段与边界相连。然后考虑转移。
1.\(i\) 不与任何连续段相连,那么连续段数+1,贡献减少 \(2i\),边界情况不变,有 \(j+1-l\) 种方案。
2.\(i\) 一边与边界相连,另一边不与连续段相连,那么连续段数+1,贡献减少 \(i\),边界上的 \(l+1\),方案数有 \(2-l\)。
3.\(i\) 一边与连续段相连,另一边不与连续段相连,那么连续段数不变,贡献不变,\(l\) 也不变,有 \(2j-l\) 种方案。
4.\(i\) 一边与边界相连,另一边与连续段相连,那么连续段数不变,贡献 \(+i\),边界的 \(l+1\),有 \(2-l\) 种方案。
5.\(i\) 两边都与连续段相连,显然连续段数减少 1,产生 \(2i\) 的贡献,边界情况不变,有 \(j-1\) 种方案。
总复杂度 \(O(n^4)\)。
坑点:小数点后30位要用__float128,边转移边除以 \(i\),以免除以 \(n!\) 时爆精度,还有要用滚动数组。
trick:遇到绝对值可以考虑从小到大的插入来计算贡献。
P3734 [HAOI2017]方案数
题意:一个人从 \((0,0,0)\) 走到 \((n,m,r)\),每一步可以 \((x,y,z)\rightarrow(x',y,z)(x\&x'=x)\) 或 \((x,y,z)\rightarrow(x,y',z)(y\&y'=y)\) 或 \((x,y,z)\rightarrow(x,y,z')(x\&z'=z)\),其中有 \(m\) 个障碍点,求方案数。
思路:考虑容斥,用全部的走法减去不合法的走法。先考虑怎么求全部走法。设 \(dp[i][j][k]\) 表示走到点 \((x,y,z)\) 满足 \(popcount(x)=i,popcount(y)=j,popcount(z)=k\) 的方案数,转移时
先把所有关键点按字典序排序,然后依次枚举每个障碍点 \(i\),设 \(f[i]\) 表示不经过任何障碍点到达 \(i\) 的方案数,因此:
其中 \(to(j,i)\) 表示从障碍点 \(j\) 到障碍点 \(i\) 的方案数,则
复杂度 \(O(\log^4n+m^2)\)。
trick:正难则反。
CF1268E Happy Cactus
题意:给定一个无向仙人掌,边权互不相同,定义 \((u,v)\) 是好的当且仅当存在从 \(u\) 到 \(v\) 的路径满足边权单调递增,对于所有 \(u\) 求出有多少 \((u,v)\) 是好的。
思路:首先考虑如果是一棵树怎么做。我们从大到小加边,设 \(f_i\) 为答案,每次加的边 \((x,y)\) 的两端在之前必然无法到达,所以 \(u\) 能到的点多了 \(v\) 和 \(v\) 能到的点,即 \(f_x=f_y=f_x+f_y+1\)。
然后考虑一般的情况。我们发现,有可能加边的两端已经互相可达,这时会出现重复,而这种情况只可能出现在最小边的两端可以合法到达最大边的环中,设最大边 \((u,v)\) 的一段为 \(u\),那贡献要减去加入这条边时的贡献,即 \(f_x=f_y=f_x+f_y+1-g[u]\),其中 \(g_u\) 为加入这条边时的贡献,即 \(g_u=f_u+1\)。先求出点双然后判断每个点双是否合法即可。
trick:从特殊情况入手。
CF1239E Turtle
题意:给一个 \(2\times n\) 的棋盘和 \(2\times n\) 个数,给出把这 \(2\times n\) 个数填入棋盘的方案,使得从左上角只向下或右走走到右下角遇到的数的和的最大值最小。
思路:先找一些性质。首先,假设选好了一行的数,那么肯定是第一行从小到大,第二行从大到小。
假设在第 \(k\) 列转向的答案为 \(ans_k\),那么 \(ans_k=ans_1+\sum\limits_{i=1}^{k-1}w_i\),其中 \(w_i=a_{i+1}-b_i\),即从第 \(k\) 位开始转向的改变量。容易知道 \(w\) 是递增的,而一开始的 \(w_1\) 有可能小于 0,那么只有可能在 \(k=1\) 或 \(k=n\) 时取到最大值。
接着,假设我们选定了 \(a_1,b_n\),那么我们就要选出 \(n-1\) 个数使得 \(\max(\sum a_i+a_1,\sum b_i+b_n)\) 最小,而 \(\sum a_i+\sum b_i\) 的值是确定的(记为 \(S\)),假设一个方案 \(\sum a_i=x\),那 \(\sum b_i=S-x\),所以最接近 \(\left\lfloor\dfrac{S}{2}\right\rfloor\)的一组方案是最优的。这样,我们枚举 \(a_1,b_n\),就得到了 \(O(n^4\sum a_i)\)的做法。
考虑优化。我们要让最大值最小,那么一定要让做贡献的数尽量小,所以一个很直观的想法是把最小值和次小值放在\(a_1\)和\(b_n\),这样就可以降低复杂度了。
复杂度 \(O(n^2\sum a_i)\)。
trick:贪心的思路在 DP 中经常出现。
P1169 [ZJOI2007] 棋盘制作
题意:找到最大的 01 交替的子矩阵。
思路:悬线法模板题。
悬线的定义是每个点向上走直到遇到障碍或边界,那么每个点都对应着一条悬线,而且每条悬线对应着一个高度等于悬线高度的矩形。
悬线法就是找出每个点对应的悬线高度和能往左右最远能够拓展多少,思想和动态规划是类似的,就是求出当前行横向左右拓展多少,然后和上一行取个交,就是这个点对应的悬线可以拓展多少。
P1758 [NOI2009] 管道取珠
题意:一开始,左侧上下两个管道分别有一定数量的两种小球,而右侧输出管道为空。每一次操作,可以从左侧选择一个管道,并将该管道中最右侧的球推入右边输出管道。假设上管道中有 \(n\) 个球, 下管道中有 \(m\) 个球,则整个游戏过程需要进行 \(n+m\) 次操作,即将所有左侧管道中的球移入输出管道。最终 \(n+m\) 个球在输出管道中从右到左形成输出序列。假设最终可能产生的不同种类的输出序列共有 \(K\) 种,其中:第 \(i\) 种输出序列的产生方式(即不同的操作方式数目)有 \(a_i\) 个。要求 \(\sum a_i^2\)。
思路:考虑 \(\sum a_i^2\) 的组合意义,可以当成是两个人操作得到同样的输出序列的方案数,于是可以设 \(f[i][j][k][l]\) 表示第一个人取了 A 中前 \(i\) 个,B 中前 \(j\) 个,第二个人取了 A 中前 \(k\) 个,B 中前 \(l\) 个的方案数,因为 \(i+j=k+l\),于是可以省掉一维,再把第一维滚掉即可。
trick:贡献中含有平方可以考虑组合意义,就是两个人操作。
[AGC016E] Poor Turkeys
题意:有 \(N\) 只火鸡, 有 \(M\) 个人, 每人指定了两只火鸡 \(x\) 和 \(y\) 。
1.若 \(x\) 和 \(y\) 都活着, 那么这个人将会等概率地随机吃掉一只
2.若 \(x\) 和 \(y\) 恰好活着一只, 那么这个人将会吃掉活着的这只
3.若 \(x\) 和 \(y\) 都已经死亡, 那么只好什么都不做
注意,第 \(1\) 个人到第 \(M\) 个人每个人依次行动
求有多少个 \((i,j)\) 满足在最终时刻第 \(i\) 只火鸡和第 \(j\) 只火鸡可能都还活着
思路:有一个很巧妙的状态:设 \(f[i][j]\) 表示如果最后要留下 \(i\),那么是否要吃掉 \(j\)。
发现正着做不好确定,于是考虑时光倒流。
对于当前的火鸡 \(a,b\),如果有 \(f[i][a]=f[i][b]=1\),那么说明 \(a,b\) 都得留下,而这个人有必须吃掉 \(a,b\) 中的一个,于是无论怎样都不能留下 \(i\);如果 \(f[i][a]\) 和 \(f[i][b]\) 中有为 1 的,那么另一个现在一定要被吃掉才能留下为 1 的那个,于是要把另一个变成 1;否则就不用处理。
最终如果对于两只火鸡不存在另一只满足要这另一只被吃掉才能留下这两只,这两只就可以保留。
trick:如果要求的是两个元素能否留下,可以设计状态为如果要保留第一个,是否已经不能留下另一个。
[AGC015E] Mr.Aoki Incubator
题意:数轴上有 \(N\) 个点,每个点初始时在位置 \(X_i\),以 \(V_i\) 的速度向数轴正方向前进
初始时刻,你可以选择一些点为其染色,之后的行走过程中,染色的点会将其碰到的所有点都染上色,之后被染上色的点亦是如此
在所有 \(2^N\) 种初始染色方案中,问有多少种初始染色方案,能使得最终所有的点都被染色
思路:假设按 \(v\) 升序,那么一个点 \(i\) 染色的区域一定是一段区间 \([l,r]\),其中 \(l\) 是满足 \(x_l\ge x_i\) 最小的 \(l\),\(r\) 是满足 \(x_r\ls x_i\) 的最大的 \(r\),于是问题就是有多少种选择区间的方案使得区间的并是全集。
此时可以发现 \([l,r]\) 是随 \(x_i\) 的增大而不降的,那么就可以很方便地 DP,设 \(f[i]\) 表示考虑前 \(i\) 个区间且选了第 \(i\) 个区间的方案数,那么所有覆盖了 \(l_i-1\) 的都可以转移到 \(i\),而这也是一段区间,可以用前缀和优化,这样 DP 复杂度就是 \(O(n)\) 的。
CF983C Elevator
题意:一个 9 层的楼有一个可以容纳 4个人的电梯,你要管理这个电梯。
现在各层楼上有一些在排队的人,你知道他们在哪层要到哪层去。你也知道到电梯门口的顺序。根据公司的规定,如果一个人比其他人早到。他也必须先进电梯(无论楼层,只凭时间)。注意人们可以随时离开电梯。
电梯有两个命令:
- 上楼或者下楼, 代价为1
- 打开当前楼层的门,所有到目的地的人会从电梯里出来,当前楼层排队的人会在不违反规定的情况下一个一个进(在电梯还有空间的情况下)(这不是天朝的电梯,不能超员)每个人用1s时间来出入电梯。
最初电梯是空的,在1楼。你需要求出最少用多长时间来吧所有人送回到目的地。最后电梯可以停在任意位置
思路:设 \(f[i][s_0][s_1][s_2]\) 考虑前 \(i\) 个人刚进入电梯的情况,剩下的人分别去 \(s_0,s_1,s_2\) ,转移时枚举下一个人进电梯之前这几个人最终去的楼层即顺序即可。
CF1566F Points Movement
题意:一个数轴上有 \(n\) 个点,\(m\) 条线段。
第 \(i\) 个点的初始位置为 \(a_i\),第 \(i\) 条线段的左右端点为 \(l_i,r_i\)。
定义一条线段 \(i\) 被标记过仅当存在时刻存在一个点 \(j\) 满足 \(l_i\leq a_j\leq r_i\)。
你可以进行下面的操作任意次:
- 选择一个点 \(i\),并使 \(a_i\) 变为 \(a_i+1,a_i-1\) 两数中的一个,代价为 \(1\)。
求使所有线段都被标记过的代价和最小值。
思路:首先,如果一个区间包含了另一个区间,那么可以不管这个区间。于是剩下来的区间的左右端点一定递增。
此时的局面一定形如是一堆点,一堆区间,再一堆点,一堆区间。一个点的轨迹一定是先往一边走,再掉头走回来到另一侧。同时,两点的轨迹不会重合,因此一个点的范围一定在上一个点和这个点的范围内,于是只考虑先哪边走,把最后往右的代价留到之后计算即可。
复杂度 \(O(n\log n)\)。
trick:费用延后计算。
P1973 [NOI2011] NOI 嘉年华
题意:给出一些区间,让你在这些区间中选出两组,两组区间不能有交。求:
① 让两份中分到区间数最小的一份,尽量得到更多的区间。
② 在第 \(i\) 个区间必须选的情况下的上一问答案。
思路:首先,区间可以离散化,于是预处理出来在 \([l,r]\) 内的区间有多少个。
对于第一问,可以设 \(pre[i][j]\) 表示前 \(i\) 个位置,选择了 \(j\) 个区间,另一组的区间数量的最大值,转移时直接枚举另一组的位置的端点 \(k\) 来转移。设 \(suf[i][j]\) 表示第 \(i\) 个位置往后的答案,求法类似。
那么第一问的答案就是 \(\min(pre[m][j],j)\) 的最大值。
然后考虑第二问。初步的想法就是钦定 \([l_i,r_i]\) 必须选一组,然后枚举两边选了多少,设 \(f[l][r]\) 表示钦定选 \([l,r]\) 的答案,那么有:
这样一个区间的答案就是 \(\max\limits_{[l,r]\in[x,y]}f[x][y]\)。
现在问题是求 \(f[l][r]\) 是 \(O(n^4)\) 的,考虑怎么优化。对于上面的式子,我们发现当 \(y\) 的决策关于 \(x\) 的决策有单调性,即当 \(x\) 增大时,最优的 \(y\) 不会增大,于是把 \(y\) 从大往小扫即可。
trick:决策单调性。
P1864 [NOI2009] 二叉查找树
题意:给你一棵 treap,你可以通过改变树的形态使得树上所有节点子树访问次数和的和最小,改变形态有代价。
思路:根据二叉查找树的性质,这棵树的中序遍历就是所有点从小到大排序后的结果,而这是无法更改的,可以从这个方面入手。
题目中说可以让点权修改为任意实数,这就意味着可以有相同权值。把权值离散化一下就可以开始 DP 了。
设 \(f[l][r][k]\) 表示当前考虑中序遍历在 \([l,r]\) 中的点,整个子树的权值都 \(\ge k\) 的最小访问代价 + 修改代价。转移时枚举作为根的点 \(x\),如果 \(k\le val[x]\),有 \(f[l][r][k]\leftarrow f[l][x-1][val[x]]+f[x+1][r][val[x]]+\sum hz_i\),其中 \(hz_i\) 表示 \(i\) 的访问频度,这是不改变 \(x\) 的权值的情况;改变 \(x\) 的权值的情况就是 \(f[l][r][k]\leftarrow f[l][x-1][k]+f[x+1][r][k]+K+\sum hz_i\)。复杂度 \(O(n^4)\)。
trick:寻找不变量,在本题中 treap 不变的就是中序遍历
P3643 [APIO2016] 划艇
题意:给出序列 \(a,b\),求有多少序列 \(c\),满足 \(c_i=-1\) 或者 \(c_i\in[a_i,b_i]\),且非 -1 的部分单增。
思路:先考虑所有 \(a_i,b_i\) 分别相等的情况,可以设 \(f[i][j]\) 表示考虑前 \(i\) 个数,最大值为 \(j\) 的方案数,然后发现 \(f[i][j]\) 是 \(i\) 次多项式,那么就可以用拉格朗日插值。
考虑一般的情况,发现每个区间都是 \(n\) 次多项式,于是可以分别用拉格朗日插值来求。复杂度 \(O(n^3)\)。
trick:状态关于是关于一维的多项式,可以用拉格朗日插值优化。
P3239 [HNOI2015] 亚瑟王
题意:给 \(n\) 个技能,每个技能有权值,有 \(r\) 轮,开始时 \(j=1\),每张牌有 \(p_j\) 的概率被选择,如果选择就进入下一轮,否则就令 \(j+1\)。一个技能只能用一次,求技能的权值和的期望。
思路:根据期望的线性性,可以把每个技能的期望加起来,那么可以转成求每个技能被使用的概率 \(a[i]\)。
对于第一个,不出的概率是 \((1-p[1])^r\),即每次到第一个技能都不选的概率。
对于其他牌,因为如果选择了就会直接跳过,我们并不好直接处理。考虑设 \(f[i][j]\) 表示前 \(i\) 个技能中选了 \(j\) 个的概率。那么对于第 \(j\) 个技能,如果前 \(i-1\) 轮选了 \(j\) 张个技能,那么有 \(j\) 轮不会考虑第 \(i\) 个技能,有 \(r-j\) 轮会考虑,于是有 \(a[i]=\sum f[i-1][j](1-(1-p[i])^{r-j})\)。
现在考虑怎么求 \(f[i][j]\)。
如果最终没有选第 \(i\) 张,那么有 \(f[i][j]=f[i-1][j](1-p[i])^{r-j}\)。
如果选择了,就有 \(f[i][j]=f[i-1][j-1](1-(1-p[i])^{r-j+1})\)。
复杂度 \(O(nTr)\)。
trick:期望的线性性。
P3251 [JLOI2012] 时间流逝
题意:给定 \(n\) 种价值不同的元素,你需要维护一个可重集(开始是空集)。每一步你有 \(p\) 的概率删除集合中一个价值最小的元素,如果当前是空集则这种情况概率为
0;否则你将等概率的获得一个元素,满足这个元素的价值不大于任何一个已经获得的元素的价值。求达到(或超过)给定的一个阈值 T 所需的期望步数。
思路:设 \(f(S)\) 表示当前集合为 \(S\),要到达阈值的期望步数,那么记 \(P\) 为 \(S\)\ \(\min{S}\),后继为 \(suf(S)\),那么转移就是 \(f(S)=1+pf(P)+\dfrac{1-p}{|suf(S)|}\sum\limits_{T\in suf(S)}f(T)\)。
我们发现这个形式就是树上高斯消元的形式,可以套路的把 \(f(S)\) 写成 \(kf(P)+b\),这样一遍 DFS 就可以解决。
对于所有集合,我们发现我们只关心和以及最小值,那么可以在 dfs 时只维护这两个值,然后暴力搜索即可。
trick:如果消元的模型中具有唯一的前驱/后继,可以把一个元素表示成前驱/后继的线性组合,这样就可以递推出答案。
P9522 [JOISC2022] 错误拼写
题意:给定字符串,长度钦定为 \(n\),定义 \(s_i\) 表示去掉第 \(i\) 位后的长度为 \(n-1\) 的字符串。给出 \(m\) 条限制 \((x,y)\),表示字典序 \(s_x<s_y\) 求有多少种字符集为小写字母的满足所有限制的字符串。
思路:设 \(t_i\) 表示 \(s_i\) 和 \(s_{i+1}\) 的大小关系,那么限制 \((i,j)(i>j)\) 代表 \([i,j)\) 的第一个不等号是 \(>\),\((i,j)(i<j)\) 代表 \([j,i)\) 的第一个不等号是 \(<\)。
然后考虑 DP。设 \(f[i][j]\) 表示前 \(i\) 个字符满足所有 \((l,r)(l\le i\le r)\) 的条件且以这字符 \(j\) 结尾的方案数,可以用前缀和优化转移。复杂度 \(O(n|\sum|+m\log m)\)。
trick:转化限制。
P7962 [NOIP2021] 方差
题意:给定长度为 \(n\) 的非严格递增正整数数列 \(1 \le a_1 \le a_2 \le \cdots \le a_n\)。每次可以进行的操作是:任意选择一个正整数 \(1 < i < n\),将 \(a_i\) 变为 \(a_{i - 1} + a_{i + 1} - a_i\)。求在若干次操作之后,该数列的方差最小值是多少。请输出最小值乘以 \(n^2\) 的结果。
思路:首先考虑这个操作的实质是什么。考虑原数列的差分序列,那么操作后的差分序列会从 \(a_i-a_{i-1},a_{i+1}-a_i\) 变成 \(a_{i-1}+a_{i+1}-a_i-a_{i-1},a_{i+1}-(a_{i-1}+a_{i+1}-a_i)\),就是 \(a_{i+1}-a_i,a_i-a_{i-1}\),即交换了差分数列的相邻两项。于是我们要求的是把差分数列任意排列后方差的最小值。
设 \(d_i=a_{i+1}-a_i\),那么根据方差的式子,有:
于是就可以发现当 \(d_i\) 呈单谷的时候答案最小。那么我们要决策的就是每个差分值放在谷的哪边。
考虑设 \(f[i][x]\) 表示已经考虑了前 \(i-1\) 个差分值,此时 \(a\) 的和为 \(x\) 时的最小平方和,记 \(s_i=\sum d_i\),那么有:
- 放左边,有 \(f[i+1][x+d_ii]\leftarrow f[i][x]+2d_ix+d_i^2i\)
- 放右边,有 \(f[i+1][x+s_i]\leftarrow f[i][x]+s_i^2\)
这样做是 \(O(n^2a)\) 的,过不去。我们发现,\(d_i=0\) 时对答案没有贡献,可以直接跳过,于是复杂度变成了 \(O(\min(n,a)na)\) 的。
trick:对于无用的元素,可以直接去掉。
P8290 [省选联考 2022] 填树
题意:给一棵树,每个点的点权要么是 0,要么是 \([l_x,r_x]\) 中的数,你要选择一条链,让这条链上的点权不为 0,求所有可能的满足非 0 极差不超过 \(k\) 的方案数和权值和。
思路:首先考虑朴素的暴力。我们枚举所有点权所在的区间 \([l,l+k]\),然后计算答案,因为要取到下界,就要容斥掉 \([l+1,l+k]\) 的答案。求答案的过程可以用树形 DP,每个点能选的是一个区间,那么可以算出当前点为链头和为 LCA 的答案,于是就可以做到 \(O(nV)\)。
考虑优化。我们发现对于选择的区间 \([l,l+k]\),每个点能取的范围是一个一次函数,那么总的贡献就是由这些一次函数形成的次数为 \(n\) 的多项式,这就不难想到用拉格朗日插值,代入 \(n+2\) 个点值就可以求出答案。对于求权值和,每个点能选的权值和是一个二次函数,那么总共就是 \(2n+1\) 次多项式,也可以同样处理。
复杂度 \(O(n^3)\)。
trick:如果 DP 和值域有关,且可以看成一个多项式,那么就可以考虑用拉格朗日插值优化。
[ARC098F] Donation
题意:给出一个 \(N\) 个点 \(M\) 条边的无向连通图,每个点的标号为 \(1\) 到 \(n\) , 且有两个权值 \(A_i,B_i\) 。第 \(i\) 条边连接了点 \(u_i\) 和 \(v_i\)。最开始时你拥有一定数量的钱,并且可以选择这张图上的任意一个点作为起始点,之后你从这个点开始沿着给定的边遍历这张图。每当你到达一个点 \(v\) 时,你必须拥有至少 \(A_v\) 元。而当你到达了这个点后,你可以选择向它捐献 \(B_v\) 元(当然也可以选择不捐献),当然,你需要保证在每次捐献之后自己剩余的钱 \(\geq 0\)。你需要对所有的 \(n\) 个点都捐献一次,求你一开始至少需要携带多少钱。
思路:重构树好题。
首先有一个性质,就是每个点只会在最后一次被经过的时候取到。于是我们设 \(C_x=\max(A_x-B_x,0)\),那么就相当于是每次经过的时候手上有的都不小于 \(C_x\),这就很重构树。我们把边按 \(\max(C_x,C_y)\) 加入,这样建的树就满足任意两点的 \(\max C\) 都最小。
接着考虑 DP。设 \(dp[x]\) 表示走完 \(x\) 的子树所需的最小钱数,\(sum[x]\) 表示 \(\sum b\),那么有 \(dp[x]=\min\limits_{y\in son[x]}(sum[x]-sum[y]+max(C[x],dp[y]))\),然后就做完了。
CF1428G2 Lucky Numbers (Hard Version)
题意:定义一个数的权值为:若第 \(i\) 位(个位算第 0 位)为 3 的倍数,则产生 \(\frac{x}{3}\times a_i\) 的贡献,多次询问一个数被拆成 \(k\) 个数产生的总贡献最大值。
思路:首先发现一个数位上是 3 还是 6 还是 9 是没有影响的,所以可以把 6 和 9 当成 3 来处理(减小思维难度及码量)。
接着我们发现可以把这 \(k\) 个数中的 \(k-1\) 个每一位都是 3 的倍数,然后考虑剩下的一个有数位不做贡献的数。先考虑这 \(k-1\) 个数,我们把分成 \(k-1\) 个数之和反过来看,即由这 \(k-1\) 个数组成当前数,再把每一个数位拆开,发现这可以当作一个多重背包,第 \(i\) 个物品的价值为 \(a_i\),体积为 \(10^i\),有 \(3\times(k-1)\) 个,但因为 \(k\) 很大,所以采用二进制分组优化。
再考虑剩下的一个数,就简单的当作一个分组背包来做,不过要注意这时也有可能还有 3的倍数的数位。
复杂度\(O(n\log(k)+(60?)n)\)。
trick:二进制优化多重背包。
「JOISC 2014 Day2」邮戳拉力赛
思路:很神仙的 DP。
我们最优的做法肯定是会绕圈,但是根据贪心思想,绕圈的点是会贪心匹配的,就是说如果把下行到上行看成左括号,上行到下行看成右括号,那么最终一定是一组合法括号序,于是就可以用括号序上 DP 的常见套路,记 \(f[i][j]\) 表示考虑到前 \(i\) 个车站,已经有 \(j\) 个左括号还未匹配的最小代价,转移分为直接盖邮戳,从下行经过时盖邮戳,作为左括号和作为右括号 4 种,分类转移即可。
trick:匹配 \(\rightarrow\) 括号序列,于是可以用括号序列上 DP 的常见做法。
「JOISC 2015 Day2」Keys
题意:JOI 社有 N 个社员,从 1 到 N 编号。社员出勤时间从时刻 0 到时刻 M。保证在时刻 0 和时刻 M 的时候,全体社员都在公司内。
今天,每个社员会恰好离开公司一次。第 i 个社员会在时刻 S_i 时刻离开公司,会在 T_i 时刻回到公司。保证不会同时有 2 个人同时离开或者回到公司。
JOI 社会社大楼有一个大门作为入口,所有社员必须从这个门进出公司。在公司内部可以自由的打开或者关闭这个门。但是在公司外的话,必须有钥匙才能打开或者关闭这个门。在时刻 0,门是关闭的。因此,第 i 个社员能在 T_i 时刻回到公司,当且仅当在 T_i 时刻的时候门开着,或者他有钥匙。
进门的社员和出门且有钥匙的社员可以选择是否去关门。当没有钥匙的社员离开时,他们无法锁门。
现在你有 K 把钥匙,你需要把这些钥匙给其中 K 个社员,使得第 i 个社员在 T_i 时刻都能够进入公司,并且从 0 时刻到 M 时刻,门被关着的时间最大。
思路:挺不错的题目。
容易想到考虑每一段时间的贡献。假设这一段开始是员工 A,结束是员工 B,有 4 种情况:
- A 进来,B 出去,这一段时间直接可以关门;
- A 进来,B 也进来,那么无论 A 是否有钥匙,只有 B 有钥匙这一段时间才可以关门;
- A 出去,B 也出去,和上一种类似,无论 B 是否有钥匙,只有 A 有钥匙这一段时间才可以关门;
- A 出去,B 进来,那么只有 A,B 都有钥匙这一段时间才可以开门。
我们观察贡献的形式,发现是形如如果一个人有钥匙那么会有一些时间可以关门,两个人都有钥匙那么有一些时间可以开门,如果转到图上,就等价于每个点有点权,有一些带边权的有向边,我们要给 k 个点染色,要使被染色的点的点权和加上两端都被染色的点的边权和最大。
同时我们可以发现更强的性质。对于一个点,出边和入边最多只有一条,而且起始点和终止点的度数都是 1,换言之,这张图的每个联通块都是链,那么就可以直接 DP 了。
具体地,我们设 \(f[i][j][0/1]\) 表示考虑到第 \(i\) 个点,已经给 \(j\) 个点染色,第 \(i\) 个点是否染色的最大贡献,转移时枚举 \(i-1\) 的状态和 \(i\) 的状态进行分类讨论即可。
trick:用图论建模分析转移顺序。
「JOISC 2015 Day 3」AAQQZ
题意:给定长为 \(n\) 的字符串 \(s\),你可以选择一个区间,将区间内的字符从小到大排序,求可以得到的最长回文子串长度,字符集大小为 \(n\)。
很有意思的题目。
首先容易做到 \(O(n^3)\)。考虑怎么优化。
我们先考察排序的区间和回文区间的关系。
- 如果两个区间无交,那么显然排序不会对回文串长度有影响。
- 如果排序区间包含了回文区间,那么答案就是最多的相同字符数,容易求出。
- 剩下的情况可以根据两个区间的中点的关系分成两种,这两种可以通过把回文串翻转来转化,于是只用考虑一种。
不妨设排序区间的中点在回文串中点右侧,根据排序区间和回文串右半部分的关系,可以分为 4 种,但这 4 种可以一起考虑。(下面的图都来自 官方题解)
具体地,对于前两种情况,我们枚举回文串中心,暴力往外拓展找最长的回文串,然后考虑两边的贡献;对于后两种情况,我们枚举排序区间左侧的前一个数作为的左端点,往右找到最近的比左端点前的数小的第一个数,那么最终回文串中心的一段都是这个数,然后再考虑两边的贡献。这时两种情况后面的处理大致相同了。
我们从左端往左找最长的不下降连续段,记录每个数的出现次数,然后从右端点开始往右扫。对于前两种情况,遇到小于左端点的数就停止;对于后两种情况,直到遇到比第一个遇到的比左端点小的数更小就停止。对于前两种情况,在扫描的过程中,两边的贡献就是右边从小到大第一个出现次数超过左边的数之前的数的个数;对于后两种情况,贡献就是比左端点小的数的个数加上右边从小到大第一个出现次数超过左边的数之前的数的个数(不算比左端点小的数)。
说起来比较抽象,看图就好理解了。
左侧有一个 \(2\)、两个 \(4\)、三个 \(6\),右边有两个 \(1\)、一个 \(2\)、三个 \(4\),右边的三个 \(4\) 已经比左侧的两个 \(4\) 要多了,那么能做贡献的就只有在中间的两个 \(1\),和两边的一个 \(2\)、两个 \(4\),回文串长度就是 \(3+2+3=8\)。
需要注意的是对于第一种情况,排序区间的右端点往右的部分也可能和左边形成回文串,提前预处理出 \(f[i][j]\) 表示从 \(i\) 往左,\(j\) 往右最长的相等串的长度即可。
复杂度 \(O(n^2)\)。
trick:讨论区间相对关系,分类解决。
「JOISC 2015 Day 3」Card Game Is Great Fun
题意:有 N 张扑克堆成一个栈,从上往下第 i 张花色是 \(C_i\),点数是 \(A_i\),价值是 \(V_i\)。有这样一个操作,每次可以选择拿走从上往下第 1 张或者第 3 张,拿走的牌必须和上一次拿走的花色或者点数一样。
请问如何拿牌,才能使得拿出来的牌的价值和最大。
思路:直接 DP,我们维护当前栈的前 3 个和上一个取走的卡牌,记作 \(x,y,z,p\),而要么 \(z=y+1\),要么 \(z=p+1\),我们就需要记录 3 维,在开一维 0/1 表示是哪种情况即可。复杂度应该是 \(O(n^3)\)。
trick:分析性质简化状态。
「JOISC 2016 Day 1」棋盘游戏
题意:JOI 君有一个棋盘,棋盘上有 N 行 3 列 的格子。JOI 君有若干棋子,并想用它们来玩一个游戏。初始状态棋盘上至少有一个棋子,也至少有一个空位。
游戏的目标是:在还没有放棋子的格子上依次放棋子,并填满整个棋盘。在某个格子上放置棋子必须满足以下条件之一:
这个格子的上下一格都放有棋子;
这个格子的左右一格都放有棋子。
JOI 君想知道有多少种从初始状态开始,并达到游戏目标的方案.
思路:很厉害的计数题。
先判断无解情况。如果 4 个角是空的或者第 1、3 行有连续 2 个空格就无解。这样第 1、3 行的空格就可以随意填了,相互之间没有影响。
从部分分入手。对于 subtask2,我们把空格形成的连通块拿出来,注意如果一个格子上下都有格子,那么也是可以随便填的,不能在连通块中,又根据无解的条件,可以发现现在最大的连通块大小也只有 5,可以直接暴搜,最后把所有连通块的答案拼起来。
我们还是从连通块入手。我们对第二行连续的空格求答案,然后组合起来。
设 \(f[i][j][0/1]\) 表示第 \(i\) 列,且中间格在前 \(i\) 列的空格中第 \(j\) 个放置,0/1 表示是在上下格子放好后再放还是左右格子放好后再放。记 \(t\) 为第 \(i\) 列第 1、3 行的空格数,转移分 3 种:
- 第 \(i-1\) 列和第 \(i\) 列都是上下放置,转移就是 \(f[i][j][0]\leftarrow t!\binom{j-1}{t}\sum f[i-1][k][0]\)
- 第 \(i-1\) 列左右放置,那么 \(i-1\) 要在 \(i\) 后面,有 \(f[i][j][0]\leftarrow t!\binom{j-1}{t}\sum\limits_{k\ge j-t}f[i-1][k][1]\)
- 第 \(i\) 列左右放置,那么 \(i\) 要在 \(i-1\) 后面,且 \(t>0\),我们枚举第 \(i\) 列的 \(t\) 个空格有 \(c\) 个是在 \(i\) 之前放置,记之前的空格数是 \(tot\),\(g[i][j]\) 表示 \(i\) 个棋子插入 \(j\) 个已放置的棋子中的方案数,有 \(f[i][j+c][1]\leftarrow g[t][j-1]t!\binom{t}{c}\binom{cnt-j+1+t-c}{t-c}\sum\limits_{k<j}f[i-1][k][0]\)
复杂度 \(O(n^2)\)。
trick:分析性质,从特殊情况入手。
「JOISC 2017 Day 3」长途巴士
思路:推出了最终会选择一些区间的性质,感觉可以 \(O(n^2)\) DP,但是不太能优化。
考虑 \(D=T\) 的条件,就是说我们不会让 \([i,n]\cup [1,j]\) 的一段人下车,不用考虑环的情况。
设 \(f[i]\) 表示考虑到 \(i\) 的最优解。写出 DP 转移式:
- 不把 \(i\) 赶下车,那么留下来需要喝 \(\left\lfloor\dfrac{X}{T}\right\rfloor+[X\pmod T\ge D_i]\) 次水,转移是 \(f[i]\leftarrow f[i-1]+W(\left\lfloor\dfrac{X}{T}\right\rfloor+[X\bmod T\ge D_i])\);
- 把 \(i\) 赶下车,那么我们枚举赶下车的一段人 \((j,i]\),那么我们需要知道这些人最早可以在第几轮被赶下车,记为 \(mint_i\),那么有 \(mint_i=\min\limits_{D_i<(S_j\bmod T)<D_{i+1}}\left\lfloor\dfrac{S_j}{T}\right\rfloor\),转移就是 \(f[i]\leftarrow f[j]+preC[i]-preC[j]+W(i-j)mint_i\)。
考虑怎么优化后一种转移。我们发现这就是斜率优化的形式,不过 \(mint_i\) 不满足斜率单调,于是需要用二分栈来优化,可以做到 \(O(n\log n)\)。
trick:斜率优化。
「JOISC 2018 Day 1」帐篷
思路:简单计数题。
显然可以想到按行 DP,然后维护当前还可以放帐篷有几列。设 \(f[i][j]\) 表示考虑了前 \(i\) 行,有 \(j\) 列还可以放帐篷,转移有 4 种:
- 这一行不放帐篷,\(f[i][j]\leftarrow f[i-1][j]\);
- 这一行放两个帐篷,\(f[i][j]\leftarrow f[i-1][j+2]\binom{j+2}{2}\);
- 这一行放一个帐篷,且以后不会和其他帐篷在同一行,\(f[i][j]\leftarrow 4f[i-1][j+1](j+1)\);
- 这一行和这一列之前一行放帐篷,\(f[i][j]\leftarrow f[i-2][j+1](j+1)(i-1)\)。
复杂度 \(O(nm)\)。
「JOISC 2018 Day 3」安全门
题意:一个括号串是好多当且仅当将一个子串取反后是合法括号串,现在有一个带通配符的括号串,求有多少个好多括号串可以匹配上。
思路:巨大 DP。
首先自然是把左括号看成 1,右括号看成 -1,那么条件就是翻转(同时反转)前后前缀和非负。
那么现在分成 4 种,两侧都合法,翻转前合法翻转后不合法,翻转前不合法翻转后合法,翻转前后都不合法。
第一种情况,等同于是合法括号串,容易 \(O(n^2)\) 解决。
第二种情况和第三种情况本质相同。
记前缀和为 \(s_i\),我们要翻转的区间肯定覆盖了第一个 \(s_i<0\) 的位置 \(p\),而且显然是 \(s_i\) 越大越好,那么 \(s_l\) 就是 \(s_1\sim s_p\) 中的最大值。
对于 \(i>r\) 的部分,翻转后为 \(s_i-s_n\),显然是合法的。
记 \(s_l=a,s_n=b\),那么有 \(s_r=a+\dfrac{b}{2}\)。如果 \(a-\dfrac{b}{2}\ge 0\),那么 \((l,p]\) 中存在满足条件的 \(r\)。否则就需要满足 \(s_p\sim s_r\) 中最大值不超过 \(2a\)。记 \(t_i=s_i-s_n\),那么有 \(t_r=a-\dfrac{b}{2}\),限制是 \([p,r]\) 中 \(t_i\) 最大值不超过 \(2(a-\dfrac{b}{2})\)。
于是可以枚举 \(a-\dfrac{b}{2}\) 然后 DP。前缀需要记录当前和历史最前缀和,可以维护 \(f[i][a]\) 表示 \(s_i=-1\) 且 \(\max s_i=a\);后缀可以维护 \(g[i][j][k]\) 表示 \(t_i=j\),\(k\in\{0,1\}\) 表示是否确定了 \(r\)。
最终答案是 \(\sum\sum f[i][a]g[i][-b-1][a+\dfrac{d}{2}<0]\)。
然后是第四种情况。我们找到第一个 \(s_i=-1\) 的位置 \(p\) 和 \(s_i-s_n=-1\) 的最大的位置 \(q\),设 \(s_1\sim s_p\) 中最大值为 \(a\),\(s_n=b\),\(s_q\sim s_n\) 中最大值为 \(c\),如果 \(a+\dfrac{b}{2}\le c\),我们以 \(s_1\sim s_p\) 中最大值位置为左端点,取 \(s_q\sim s_n\) 中第一个等于 \(a+\dfrac{b}{2}\) 为右端点,然后就可以类似上一种的情况转移。如果 \(a+\dfrac{b}{2}>c\),翻转后就是 \(a+\dfrac{b}{2}\le c\) 的情况。最后要减去 \(a+\dfrac{n}{2}=c\) 的情况。
复杂度 \(O(n^3)\)。
trick:经典的把左括号看成 1,右括号看成 -1。