2026.7 做题纪要
2026.7 做题纪要
(以下是 2026.6.25 ~ 2026.6.30 陕西省队集训内容)
dp 大家都知道,要满足最优子结构和无后效性。然后 dp 题整体就是两个部分:状态设计和 dp 转移。
状态设计的几个技巧:划分子问题,延迟钦定,统计去重;
转移优化的几个技巧:虚状态,保留有效状态,合并等价状态。
划分子问题显然要满足无后效性,对于计数来说就是不重不漏。一般子问题的划分有明显的阶段,这需要我们发现题目的性质来求解。
UOJ22 外星人
这个题是有一个明显的阶段的,假设一次取模完之后当前数字为 \(x\),那么所有 \(>x\) 的数可以直接放到当前位置的后面,然后我们就只需要考虑 \(\le x\) 的数字了。那么我们把这个当作子问题,设 \(f_i\) 表示当前数字为 \(i\),且只考虑原数组中所有 \(\le i\) 的数的方案数。
转移的时候枚举一个 \(j\),然后将所有 \(> i\bmod j\) 的数直接放到 \(j\) 后面,这样我们就可以转移到 \(f_{i\bmod j}\) 作为下一个子问题了。复杂度 \(O(nV)\)。
有一些题目的等价类划分方式(也就是两个方案不同的判定)和我们传统的计数方法不一样时(具体来讲应该是包含关系),我们通常设法使其一致。常见的方法是在传统的计数中添加一个限制,使得每个等价类里只有一个元素满足限制,这样我们只计算满足要求的元素个数就是等价类个数。
CF720D Slalom
如果计数的是路径条数的话有一个简单的 dp,设 \(f_{i,j}\) 表示走到 \((i,j)\) 的方案数,直接转移即可。
但是现在我们的等价类划分并不是这样,考虑找一个组合条件,一般的套路是找边界或者极端值。在这个题里,对于一条路径,我们把它向下向右拉到障碍的边界上,把这个当作等价类的代表元。换句话说,假设我们已知那些障碍在当前路径下面,我们每一次优先往右走,如果走不了再考虑往上走;而一条路径显然只对应一种选障碍的方案。于是这是一个双射,对这种路径计数即可。
考虑转移,再来看这种路径的性质,注意到每一次向上走然后向右拐之后,走到的一定是某个障碍的左上角的上面。所以:
-
如果 \((i,j-1)\) 是某个障碍的左上角,
\[f_{i,j}=\sum_{k\le j} f_{i-1,k} \]当然要保证 \((i-1,k)\) 一直向上走的过程中没有障碍。可以用
set维护 \(i-1\) 列上所有的障碍区间,二分找到合法的 \(k\) 对应的区间。 -
否则,
如果 \((i,j)\) 不是障碍,有 \(f_{i,j}=f_{i-1,j}\);
否则 \(f_{i,j}=0\)。
所有转移操作都可以用线段树维护,总复杂度 \(O(n\log n)\)。
AGC013D Piling Up
注意到求出操作的方案数是容易的,设 \(f_{i,j}\) 表示 \(i\) 轮操作后剩 \(j\) 个红球的方案数。转移分讨四种情况可以得到:
这样看似很对,但是我们初值不一样的时候,得到的颜色序列也有可能是一样的,所以我们会算重。仍然考虑在等价类里面取极端值,我们发现这个 dp 的过程可以看作是在坐标系上的折线,而所有平行线就是我们的等价类。那么我们可以在每一组平行线中取最靠下的一个,它一定会和 \(x=0\) 接触,把这根线当作等价类的代表元即可。
所以我们新加一维 \(0/1\) 表示当前的红球个数有没有达到 \(0\),只统计这一维上是 \(1\) 的状态总和即可。复杂度 \(O(nm)\)。
P13272 [NOI2025] 序列变换
第一问不看,主要来看第二问。发现用第一问求出来的区间信息直接求解是错的,因为有可能划分区间方式不同,得到的最终 \(D\) 序列是相同的。依然考虑在等价类中找代表元,假如说我们现在的区间是 \(000\dots p\dots 000\),如果左侧有一段区间满足奇数位和 等于 偶数位和,那么我们可以将这一段区间剥出去,这样的话每一个 \(D\) 序列对应唯一一个划分方案。所以我们求区间保证左右没有和为 \(0\) 的段即可,暴力转移,复杂度 \(O(n^2)\)。
保留有效状态是一种常见但是不易发现的优化,主要原因来自于 “有效状态” 的定义,有可能是这个状态不合法(无法由起点走到),也有可能是根据状态之间的偏序关系删除掉那些一定不优的状态。
合并等价状态和保留有效状态是不同的,等价状态的意思是说,有一些状态在后续转移的过程中的决策是一致的,那么它们可以被缩成同一个等价类,这样减少了状态数。
AGC017F Zigzag
直接暴力 dp 每一根线是 \(O(4^n\text{poly}(n,m))\) 的,显然过不去。考虑优化,由于线之间有顺序要求,考虑轮廓线 dp,设 \(f_{i,j,S,d}\) 表示当前填完了 \(i-1\) 根线,第 \(i\) 根线走了 \(j-1\) 步,\(S\) 的前 \(j-1\) 位表示第 \(i\) 根线走的方向、后 \(n-j\) 位表示第 \(i-1\) 根线走的方向,当前第 \(i-1\) 根线的横坐标为 \(d\) 的方案数。暴力转移复杂度是 \(O(2^nn^3)\) 的,无法通过。
考虑合并相同状态,我们希望把 \(d\) 这一维合并掉。如果在第 \(j\) 次操作,\(i-1\) 和 \(i\) 走的方向一致,那么显然 \(d=0\);否则的话说明 \(i-1\) 往左而 \(i\) 往右,此时 \(i\) 能走到的范围是一个三角形,而这个三角形之外会有一部分 \(i-1\) 的折线,容易发现的是这一部分折线是无用的,把它们合并起来。具体来说就是把这个三角形之外的折线直接拉平到这个三角形上作为限制,这样的话会发现 \(d\) 始终 \(=0\),所以这一维可以删掉,复杂度 \(O(2^nn^2)\)。
CF506E Mr. Kitayuta’s Gift
首先考虑暴力 dp,这里不必考虑等价类,我们可以直接对着答案进行 dp。设 \(f_{i,l,r}\) 表示考虑了最终回文串的前 \(i\) 位和后 \(i\) 位,从左开始贪心匹配 \(s\) 匹配到了 \(l\),从右开始贪心匹配 \(s\) 匹配到了 \(r\) 的方案数。
很显然由于 \(n\) 较大,我们需要矩阵快速幂。直接做矩阵大小是 \(O(|s|^2)\) 的,此时复杂度 \(O(|s|^6\log n)\),肯定过不去。考虑这个 dp 转移在自动机视角下长什么样,实际上可以得到下图:

总共 \(O(|s|^2)\) 个节点,我们肯定要对它进行压缩,也就是找到等价状态。发现我们大部分时间都在走自环,而如果把自环去掉,我们可以发现,只要路径上红点个数和绿点个数是一样的,那么两条路径对应的方案数就是一样的。
所以把这些路径作为等价状态压缩。我们考虑求出从起点走到终点经过了 \(i\) 个红点的方案数,此时经过的绿点个数 \(j=\lfloor\frac{|s|-i}2\rfloor\) 是一个定值。这个 dp 可以直接 \(O(|s|^3)\) 求解出来。然后对于每一种 \(i\),我们可以用矩阵快速幂优化这条链的方案数,复杂度是 \(O(|s|^4\log n)\) 的。

容易发现我们要求上面若干条链的方案的总和,再压缩一下可以得到下面这个图,此时点数为 \(O(|s|)\),矩阵快速幂复杂度为 \(O(|s|^3\log n)\),可以通过。注意特殊处理字符串总长为奇数的情况,此时最后一步不能删去两个字符。
QOJ9632 联通块
一个经典结论是一个点到连通块内距离最远的点一定是两个直径端点之一。所以连通块内所有点到 \(u\) 距离 \(\le d\) 当且仅当两个直径端点到 \(u\) 距离 \(\le d\)。那么实际上合法的 \(u\) 应该是两个直径端点的 \(d\) 邻域的交,这个交是以直径中点为圆心,\(d-\frac{len}2\) 为半径的一个树上圆,其中 \(len\) 为直径长度。所以合法当且仅当 \(dis(u,x)\le d-\frac{len}2\),点分树维护即可。
然后是每次删边之后要维护连通块直径,每一次删边会把 DFS 序删成 \(O(k)\) 段,用一个栈状物维护每个连通块对应哪些段,ST 表维护 DFS 序区间上的直径,单次可以 \(O(k\log n)\) 维护出所有直径,总复杂度视实现情况大概是 \(O(n\log n)\sim O(n\log^2 n)\)。
静态树上邻域数颜色
对于每种颜色单独计算答案。对于一种颜色,把它的虚树建出来,然后设虚树上每一个点的 \(f_u\) 表示离他最近的该颜色点的距离。这样的话对于一个询问 \((x,d)\),这种颜色会被计算进去当且仅当存在 \(u\) 满足 \(f_u+dis(u,x)\le d\)。
注意到满足要求的 \(u\) 在虚树上构成连通块,考虑点边容斥,用点数减边数构造出这个颜色 \(1\) 的贡献。点的要求是容易的,直接点分树维护。边的要求就是两端的点要求同时满足,和上一题一致,两端的点的要求的交是一个定点的邻域,所以也可以点分树维护。
QOJ5020 举办乘凉州喵,举办乘凉州谢谢喵
设 \(f(x,y,d)\) 表示路径 \(x\to y\) 的 \(d\) 邻域大小,那么实际上可以注意到:
最后一个是一个单点的 \(d\) 邻域大小,点分树维护。剩下的就是求 \(f(1,x,d)\) 了。
考虑重链剖分,设 \(f_{u,d}\) 表示 \(u\) 的所有轻子树中距离 \(u\) 的距离 \(\le d\) 的点的个数,我们先直接把 \(1\sim x\) 根链上所有点的 \(f_{*,d}\) 累加,这样的话只有在链顶的父亲处会算错,我们要撤销一个子树然后加入一个子树的贡献,这就要求我们进一步求出 \(g_{u,d}\) 表示 \(u\) 子树内距离 \(u\) 的距离 \(\le d\) 的点的个数。
求 \(f_{u,d}\) 可以直接暴力枚举轻子树,这样总的大小是 \(O(n\log n)\),再加之我们要求前缀和,树状数组维护一下即可。\(g_{u,d}\) 显然是一个二维偏序,不过我们总共要求的 \(g\) 的个数有 \(O(n\log n)\) 个,所以总复杂度就是 \(O(n\log^2 n)\)。
事实上求 \(g_{u,d}\) 可以容斥一下,用 \(siz_u\) 减去它的 \(d+1\) 级儿子的 \(siz\) 之和,这个东西可以维护一个桶表示某一深度的点的 \(siz\) 和,每次差分一下就可以求出 \(u\) 子树内的某一深度的点的 \(siz\) 和。同样用这个容斥技巧维护 \(f\) 就不用求前缀和了,这样总复杂度可以干到 \(O(n\log n)\)。
P9041 [PA 2021] Fiolki 2
很神秘。很显然我们要选出若干条不交路径,考虑 LGV 引理。先假设我们确定了哪些是终点,然后判定是否可行,我们知道 LGV 引理会乘上一个 \((-1)^{\text{ord}(p)}\) 的系数,这可能导致一些方案抵消,一个经典技巧是给边权随机赋一个 \([1,P)\) 之间的随机数,其中 \(P\) 是一个大质数,这样如果行列式算出来 \(\ne 0\),我们就可以认为有一种合法的路径不交方案。
然后考虑求答案,先考虑怎么求 \(f(l,r)\)。我们求出 \(e_{u,v}\) 表示 \(u\to v\) 的路径权值之和,然后对于每个 \(>k\) 的 \(i\),设一个向量 \(V_i=\{e_{1,i},e_{2,i},\cdots,e_{k,i}\}\)。我们知道行列式非零意味着这些向量线性无关,所以实际上就是求 \(\{V_l,V_{l+1},\cdots V_r\}\) 的极大线性无关组的大小,线性基可以维护出来,这样的复杂度是 \(O(n^2 k^2)\) 的。
实际上只需要使用区间线性基的技巧就可以将这一部分优化到 \(O(nk^2)\),总复杂度 \(O(nk^2+mk)\)。
CF1770F Koxia and Sequence
这题力气大。首先根据对称性,设 \(f(i,t)\) 表示 \(a_i=t\) 的合法序列个数,那么 \(f(1,t)=f(2,t)=\cdots=f(n,t)\),所以如果 \(n\) 为偶数则答案直接为 \(0\),否则我们只需要考虑所有合法序列的 \(a_1\) 的异或和即可。
接下来拆位处理,考虑第 \(i\) 位的答案。首先注意到条件和或相关,如果要拆位处理的话应该需要容斥,所以枚举 \(y\) 的子集 \(y'\),容斥系数为 \((-1)^{|y-y'|}\),但是你发现一个事情是由于现在只有一位,所以我们只关注答案 \(\bmod 2\) 的结果,而 \(-1\equiv 1\pmod 2\),所以这个容斥系数没有用。
接下来我们钦定 \(a_1\) 的第 \(i\) 位为 \(1\),求出合法这样的序列有多少个,那么此时的条件可以写成:
- \(\sum\limits_{i=1}^n a_i+2^i =x\);
- \(a_1\subseteq (y'-2^i)\) 并且 \(\forall i\in [2,n],a_i\subseteq y'\)。
这个东西看着很难求,但是我们接下来有神之一手:Kummer 定理告诉我们 \([m\subseteq n]=\binom{n}{m}\bmod 2\),往常我们都是把右边化为左边,而现在我们要把左边化为右边!这样我们的答案可以写成:
更可怕的事情是,注意到我们的条件 \(1\) 保证了组合数下指标和固定,所以用一次范德蒙德卷积可以得到答案为:
暴力枚举 \(i,y'\) 就做完了,复杂度 \(O(n2^n)\)。
AGC035F Two Histograms
我能在没有脑子的情况下做出这道题吗?
假如我们没有观察出容斥的结论,那么这种题的传统做法应该是找到每一个等价类和某种计数方式的双射,对着另外的计数方式算就行了。这个题手玩一下可以得到如下的方式:我们考虑把每一行当做一个阶段,上方会有一些可以延伸下来的列。假设我们已知这一行最终的结果,为了构成双射我们需要当前方案唯一,那么我们尽可能多的保留上方的列,可以发现这样的方案唯一,这样的话任何一个合法棋盘只对应一种染色方案。
接下来我们具体考虑怎么延伸使得保留下来的尽可能多,那换句话说就是这一行尽可能短。首先 \(2\) 我们是不得不填的,设这一行上最后一个 \(2\) 在 \(p_1\)。但是这不一定合法,我们还需要考虑 \(p_1\) 后面,如果后面有一个 \(1\) 但是这一列上已经没有延伸下来的列了,这一行也需要延伸到这里,假设这一行最后一个这样的位置在 \(p_2\)。那么这一行的长度就是 \(\max(p_1,p_2)\)。
这样的话我们只需要利用这个方式计数,设 \(f_{i,j}\) 表示第 \(i\) 行后还剩下 \(j\) 列可以向下延伸,然后枚举上一行剩下的列数 \(k\)。然后分讨一下这一行延伸到哪:
- 如果延伸到了 \(p_1\),那么相当于延伸到 \(j\) 列中的一个,方案数为 \(j\);
- 否则意味着延伸到了一个 \(i-1\) 没有的列,方案数为 \(m-k\);
- 还有一种情况是这一列为空,方案数为 \(1\)。
所以有转移方程:
这个复杂度是 \(O(n^3)\) 的,显然过不去,不过因为式子中有组合数,可以考虑指数生成函数,具体的:
令:
则转移可以写成 \(F_i(x)=F_{i-1}(x)\times G(x)\)。那么实际上我们要求的 \(F_n(x)\) 就是 \(F_0(x)\times G(x)^n\),而实际上:
所以有:
而事实上 \(F_0(x)=m!x^m\),我们要求的答案为:
稍微改的好看点就是:
直接求解复杂度 \(O(n)\)。

浙公网安备 33010602011771号