2025.7-2025.8 做题记录
2025.7-2025.8 做题记录
7.1 - 7.11
复习段考 + 段考 + 讲评课
7.12
模拟赛
7.12 T1 - 规则制定 (单调栈)
所有区间的区间和之和容易求,拆到每个元素上即可
下面考虑如何求所有区间的最大值之和
若区间内有多个最大值,考虑将贡献放到最靠前的最大值上
求出 \(\text{lmax}_i\) 表示 \(i\) 前最靠后的满足 \(a_j \ge a_i\) 的 \(j\),\(\text{rmax}_i\) 表示 \(i\) 后最靠前的满足 \(a_j > a_i\) 的 \(j\)
\(a_i\) 作为最大值的贡献次数即为 \([\text{lmax}_i, \text{rmax}_i]\) 中长度 \(\ge 3\) 的区间个数
实现时可以用单调栈 \(O(n)\) 维护
7.12 T2 - 作文批改 (随机权 + 和 Hash)
考虑星战的做法,给字符 \(c\) 赋随机权值 \(v_c\),和哈希判断是否相同
不过这里有个"出现次数为 \(k\) 倍",因此考虑先求出 \(t\) 的哈希值 \(B\) (不取模),以 \(B\) 为模数维护 \(s\) 的前缀哈希值 \(pre_i\)
此时子串 \(s_{[l, r]}\) 符合要求等价于 \(pre_r = pre_{l-1}\)
由生日悖论,值域为 \(V\) 时出现冲突的期望抽样次数为 \(O(\sqrt V)\);此处抽样次数为 \(10^7\),需保证 \(B \ge 10^{14}\)
实现细节:
- 可以根据 \(|t|\) 动态调整随机权范围
- umap 常数太大,直接 sort 后双指针比 umap 快
7.12 T3 - 赛道设计 (Adhoc)
可以通过手玩猜出 \(|R-L| \in [3, 5]\) 的结论 (起点也可以视作拐点)
下面给出严谨证明
设有 \(m\) 个拐点,则赛道为 \(m\) 边形,每个内角均为 \(90^{\circ}\) 或 \(270^{\circ}\),内角和为 \((m-2) \times 180^{\circ}\)
由于所有 \(L / R\) 对应的内角只能均为 \(90^{\circ}\) 或 \(270^{\circ}\),且两者对应度数不同,可得 (不妨设 \(L\) 对 \(90^{\circ}\) )
\(L \times 90^{\circ} + R \times 270^{\circ} = (L+R-2) \times 180^{\circ}\)
整理即得 \(L-R = 4\)
由于连续的 \(LR / RL\) 不会带来方向上的变化,可先将其删去;调整起点,最后一定能剩下 \(LLLL / RRRR\)
这个构造是简单的;之后重新往里添加 \(LR / RL\) 的过程相当于选两个点,将路线从直线改成三折
每次重新调整其他所有点的位置可以做到相对位置不变;时间复杂度 \(O(Tn^2)\)
实现咕咕咕
LOJ6538 烷基计数 加强版 加强版 (群论 + 生成函数 + 牛迭)
见群论基础 - 学习笔记 Blog
P6597 烯烃计数 (群论 + 生成函数 + 牛迭)
见群论基础 - 学习笔记 Blog
P6598 烷烃计数 (群论 + 生成函数 + 牛迭)
见群论基础 - 学习笔记 Blog
7.13
开摆 + 补 whk
7.14
P3391 【模板】文艺平衡树
本题中,我们并不关心平衡树每个节点具体的值是否满足 BST 的性质
此处平衡树的每个节点对应原序列的下标,换句话说,我们按照下标建树
考虑如何刻画翻转操作;若我们能够分裂出 \([l, r]\) 对应的子树,将每层的节点全部左右调换即可 (可理解为值换了,对应下标不换)
直接换显然会 TLE;考虑线段树懒标记的思想,给要全部调换的子树的根打 tag,用到的时候再真正交换儿子
现在考虑如何分裂出 \([l, r]\) 对应的子树
注意到序列下标始终满足 BST 的性质,考虑按排名分裂的思想,只不过此处是按子树大小分裂;具体的:
- 分裂出 \([1, l-1]\) 与 \([l, n]\) (按大小为 \(l-1\) 分裂)
- 将 \([l, n]\) 分裂为 \([l, r]\) 与 \([r+1, n]\) (按大小为 \(r-l+1\) 分裂)
P3835 【模板】可持久化平衡树
仍然套路的考虑记录每个版本的根,复制所有修改过的节点
具体的,为避免直接修改历史版本的值,在每次 split 与 merge 时对将进行分裂 / 合并的节点复制一份再操作即可
实现时,无须复制递归处理的另一侧 (递归处理子树时自然会复制的)
P5338 [TJOI2019] 甲苯先生的滚榜 (平衡树)
把 int 换成结构体,"修改通过题数和罚时"可变成先删除后插入
注意人的编号无法直接和 Treap 中的值建立联系,需要开个辅助数组
P4146 序列终结者 (平衡树 - 区间加 + 区间翻转 + 区间 max)
考虑在 Treap 上的节点维护当前下标的值 + 子树对应区间的最大值
区间翻转仍然通过打 tag 维护,区间加相当于又多了个 tag;修改时直接分裂出区间打上 tag 即可
注意,打 tag 的顺序是:
- 打 tag 前做好当前节点的修改
- 给当前节点打上 tag
- pushdown 时做好儿子节点的修改,把 tag 传到儿子节点上
为什么不能在 pushdown 时只做当前节点的修改,pushdown 儿子时再修改儿子?
因为 merge 时实际上非递归侧的儿子不会被 pushdown,需要在 pushdown 当前节点时就处理好儿子节点的信息
P3765 总统选举 (平衡树 + 随机化 - 带修绝对众数)
先考虑如何判断区间 \([l, r]\) 是否存在绝对众数
考虑随机化;随机 \(\text{lim}\) 次,每次随出一个位置 \(i\),判断 \(a_i\) 在 \([l, r]\) 中出现次数是否超过一半
这样单次失败概率是 \(\displaystyle \frac{1}{2}\),\(\text{lim}\) 次就是 \(\displaystyle \frac{1}{2^\text{lim}}\),大概可以接受
那么问题在于如何快速统计 \(a_i\) 在 \([l, r]\) 中的出现次数
先考虑不带修怎么做
对每个值 \(v\) 开 vector 记录其出现位置 (显然有序),查询时直接二分即可
带修相当于要支持删除 + 插入,二分可以变成查排名;那么对每个值 \(v\) 开一棵平衡树即可
实现时可以取 \(\text{lim} = 13\)
P2596 [ZJOI2006] 书架 (平衡树 - 下标移动)
操作 \(4\) 是根据值查排名,操作 \(5\) 是根据排名查值,明示平衡树;问题在于我们没办法直接建立编号与位置的关系
考虑给每本书赋值 \(v\) 代表其相对位置大小 (由于是相对的,没必要时刻连续)
为建立编号与 \(v\) 的联系,考虑开两个 map,第一个从编号映射到 \(v\),第二个从 \(v\) 映射到编号
接下来考虑每个操作:
-
Top,从 map 中取出编号对应的 \(v\),将 \(v\) 修改为全局编号最小值 \(-1\) 即可实现时可以维护全局编号最小值,"修改"就是删除 + 插入
-
Bottom,类似的,维护全局编号最大值,删除 + 插入即可 -
Insert,本质上是与前驱或后继交换;注意到 Treap 上节点对应的 \(v\) 没变,变的只有 \(v\) 与编号的对应关系从 map 中取出 \(v\),查出前驱 / 后继,将 map 上两组对应关系交换即可
-
Ask,从 map 中取出 \(v\),根据值查排名即可 -
Query,根据排名查值,最后通过 map 从 \(v\) 映射回编号即可
P2566 [SCOI2009] 围豆豆 (区间DP + 计算几何)
先考虑如何判断点在多边形内
由计算几何知识,考虑从点引一条射线,若与多边形交点个数为奇数则包含,反之不包含
此处多边形的边只能是水平 / 竖直的,为方便判断,钦定每个点引射线的方向都是水平向右的
因此,在平行于射线 (水平方向) 移动时不计算交点,竖直移动时才计算
设 DP 状态 \(f_{x, y, S}\) 表示走到 \((x, y)\),点的包含关系为 \(S\) 时的最短步数;其中,\(S_i\) 代表第 \(i\) 个点引出的射线与多边形交点的奇偶性
接下来考虑如何计算 \(S\)
直接按"每经过一次点右边的格子就算一次"的方式考虑是不可取的,因为每个格子有"进入"、"离开"两次操作
注意到按同一方向 (均上 \(\rightarrow\) 下 / 均下 \(\rightarrow\) 上) 只应当被算一次,因此考虑从经过格子的两个方向上统计;具体的:
-
当从上 \(\rightarrow\) 下进入格子时,统计 \(1\) 次
-
当从下 \(\rightarrow\) 上离开格子时,统计 \(1\) 次
为什么这里是"离开"而非"进入"?因为要想形成多边形,必然要跨过格子所在水平线,只有离开格子时才是真正跨过去
实现时可以枚举起点,bfs 进行转移,第二次走到起点时结束
初始状态为 \(f_{x, y, 0} = 0\);设 \(v_S\) 为 \(S\) 状态包含的点的价值和,答案为 \(\max (v_S-f_{x, y, S})\)
时间复杂度 \(O(n^2m^22^D)\)
7.15
P3401 洛谷树 (树剖 + 拆位)
考虑如何维护子路径 xor 和
注意到边权 \(\le 2^{10}\),考虑拆位,开 10 棵线段树
考虑经典转化,将路径 xor 变为两个根链 xor,维护每个点到根的 xor 值
记 \(cnt_0\) 为 \(u, v\) 间路径中第 \(i\) 位为 \(0\) 的点的个数,\(cnt_1\) 为 \(u, v\) 间路径中第 \(i\) 位为 \(1\) 的点的个数,贡献即为 \(cnt_0 \times cnt_1 \times 2^i\)
那么维护区间中根链 xor 当前位为 \(0\) / \(1\) 的点的个数即可
对单点改,若 \((u, v)\) 更改后边权的第 \(i\) 位与更改前不同,则相当于子树区间 \(0\) / \(1\) 翻转
综上,上树剖 + 支持区间加区间 \(0\) / \(1\) 翻转区间和的线段树即可
P2542 [AHOI2005] 航线规划 (树剖 - 删边求割边个数)
首先考察是树怎么做
显然,此时 \(u, v\) 间的割边个数即为两点树上路径中的边的个数;这启示我们给边赋 \(1\),做树剖 + 路径区间查
在树的基础上给 \(u, v\) 加边,相当于让两点树上路径中的边全部处于环中
此时这些边不再产生贡献,做路径区间推平即可
综上,上树剖 + 支持区间加区间推平区间和的线段树即可
P3676 小清新数据结构题 (树剖 + 线段树 - 区间加区间平方和)
首先考虑没有换根怎么做
先求出每个点 \(i\) 初始的子树大小 \(siz_i\),维护 \(siz_i\) 的平方和
此时单点改只会对根链上的点的子树大小产生影响,为路径区间加
显然,直接上支持区间加区间平方和的线段树即可
接着考虑换根;对于这类操作,我们一般不真换,而是分析其在保持根不动的前提下带来的变化
不妨设根换为点 \(u\):
- 对点 \(u\) 的子树中的点 \(v\),贡献不变
- 对点 \(u\),从 \(siz_u^2\) 变为 \((\sum siz_i)^2\)
- 对非根链上的点 \(v\),贡献不变
- 对根链上的点 \(v\),子树大小减少了面向 \(u\) 的部分,增加了面向根的部分
不妨设 \(1, u\) 间路径上的点为 \(1 = a_1 \rightarrow a_2 \rightarrow \cdots \rightarrow a_k = u\),点 \(i\) 原大小为 \(siz_i\),新大小为 \(siz'_i\),原答案为 \(t\),则新答案为:
显然有 \(siz'_{a_i} + siz_{a_{i+1}} = siz_1 = siz'_u\),代入,可得
那么只需维护 \(t, siz_1\) 与根链路径区间和即可;
综上,仍然是上树剖 + 支持区间加区间和区间平方和的线段树即可
P5391 [Cnoi2019] 青染之心 (树剖 - 末尾加 / 撤销完全背包优化空间)
操作序列实际上形成了一棵树,add 相当于拼个儿子,erase 相当于回退到父亲;要求所有根链的完全背包
暴力做法时空复杂度均为 \(O(qV)\),空间上无法接受;考虑如何优化
注意到若节点 \(u\) 的 DP 数组已经传到每个儿子 \(v\) 上,其本身已经没有用处了;因此考虑将某个儿子 \(v\) 的 DP 数组覆写到 \(u\) 的数组上
结合树链剖分,考虑令 \(v\) 为 \(u\) 的重儿子,最后处理 \(v\),将其 DP 数组覆写到 \(u\) 的数组上
每当我们 dfs 完一条根到叶子的链时,立刻回收空间,经过的轻儿子数是 \(\log\) 的,那么空间复杂度也是 \(\log\) 的
P5314 [Ynoi2011] ODT (树剖 + 平衡树维护轻儿子)
"第 \(y\) 小点权"明示我们使用平衡树
考虑修改;暴力做法是把路径上所有点的平衡树全改一遍,显然无法接受
考虑与上道题类似的策略,令 \(u\) 点的平衡树维护其所有轻儿子的权值
路径改时只会经过 \(\log\) 个轻儿子,只会改 \(\log\) 个平衡树 (修改就是删除 + 插入),可以接受
查询时,将点 \(u\) 的父亲 + 重儿子插进平衡树,查出答案,最后再删掉即可
实现时还需要维护一个线段树 / 树状数组查每个点本身的点权
时间复杂度 \(O(n \log^2 n)\)
7.16
BZOJ P3648 寝室管理
AGC001C - Shorten Diameter
P7215 [JOISC 2020] 首都
7.17
P5642 人造情感(emotion) (树上路径最大独立集 DP)
先假设我们可以快速求出 \(W(S)\)
对 \(f(u, v)\),由于加入 \(\text{path}(u, v)\) 后 \(W\) 变大,显然 \(\text{path}(u, v)\) 必然被选,其上的点均不能被占用
考虑将 \(\text{path}(u, v)\) 上的点及其所连的边全部删去以刻画"不能被占用";记删去路径后形成的若干树为 \(T_1, T_2, \cdots, T_k\),完全包含于 \(T_i\) 中的路径集为 \(S_i\),可得 \(w = W(S)-\sum_{i=1}^{k} W(S_i)\)
不妨令 \(1\) 为根;更进一步,易得删去路径后剩下的树只能是:
- 原来以某个点为根的子树
- 整棵树删去以某个点为根的子树后得到的树 (子树的补)
综上,考虑设计状态刻画"完全包含于某个子树 / 子树的补中的路径集的 \(W\) 值"
为求子树中的答案,我们令:
- \(f_u\) 表示仅考虑 \(u\) 子树的答案
- \(s_u\) 表示仅考虑 \(u\) 子树,且强制不占用 \(u\) 的答案
对初始值,考虑叶子 \(l\),有 \(f_l = \max_{u_i = l, v_i = l} w_i\) 且 \(s_l = 0\);我们从下向上转移:
-
\(s_u \leftarrow \sum f_v\),其中 \(v\) 是 \(u\) 的儿子
-
$ f_u \leftarrow s_u + \max { w_i-\sum_{k \in \text{path}(u_i, v_i)} (f_k-s_k) }$,其中 \(\text{lca}(u_i, v_i) = u\)
解释:考虑先强制不选 \(u\),再拼上一条经过 \(u\) 的路径 (因此将每条路径挂在其两端点的 lca 上);这条路径上的点都不能被占用,因此对路径上的每个点 \(k\),减去 \((f_k-s_k)\) 表示将 \(k\) 点从可选可不选调整至必须不选的代价
注意到,若 \(k\) 原本就不选,必有 \(f_k = s_k\);若 \(k\) 原本选,代价就是 \(f_k-s_k\),正确性没有问题
那么 \(k\) 点的代价 \(f_k-s_k\) 是否会被重复计算?其实也不会,若路径经过 \(k \rightarrow v \rightarrow u\),若之前 \(v\) 点已经考虑了强制不选 \(k\) 的代价,在 \(u\) 点转移时将 \(f_v\) 改为 \(s_v\) 时会撤去这一代价,最终也只会在 \(k\) 处算一次
考虑如何维护 \(\sum_{k \in \text{path}(u_i, v_i)} (f_k-s_k)\)
其实就是单点加 + 链求和,树剖固然可做,不过树状数组维护根链可以少个 \(\log\)
形式化的,设 \(i\) 的子树的 \(\text{dfn}\) 区间为 \([\text{dfn}_i, \text{bottom}_i]\):
- 对 \(i\) 单点加变为在 \(\text{dfn}_i\) 加,在 \(\text{bottom}_i+1\) 减 (转化为子树加,差分了一下)
- 对 \(i\) 查询相当于查 \([1, \text{dfn}_i]\)
容易发现,\(f_1 = W(S)\)
为求子树的补的答案,我们令 \(g_u\) 表示 \(u\) 子树的补的答案
在求出 \(g_u\) 后,我们给挂在 \(u\) 上的每条路径 \(\text{path}(u_i, v_i, w_i)\) 赋新权 \(w'_i = w_i+s_u-\sum_{k \in \text{path}(u_i, v_i)} (f_k-s_k)+g_u\),表示选择该条路径时,考虑子树内代价 + 子树外贡献的值 (即全局贡献的值)
对初始值,考虑根 \(1\),显然有 \(g_1 = 0\);我们从上向下转移,从 \(u\) 转移到儿子 \(v\) 时:
-
若 \(u\) 未被占用,有 \(g_v \leftarrow g_u+s_u-f_v\)
解释:\(s_u-f_v\) 表示考虑 \(u\) 除儿子 \(v\) 的其他儿子的贡献
-
若 \(u\) 被形如 "\(u\) 的后代 \(\rightarrow u \rightarrow\) \(u\) 的祖先" 方向的路径占用,令这样的路径的权 (新权) 的 max 为 \(w'\),有 \(g_v \leftarrow w'-f_v\)
由定义,这样的路径有且仅有一个端点是 \(u\) 的后代,且这个端点不是 \(v\) 的后代
考虑如何维护这样的路径
由于转移顺序是从上到下,转移到 \(g_v\) 时已经经过了 \(v\) 的所有祖先;因此考虑在每个点 \(p\) 处理挂在 \(p\) 上的路径对其后代的贡献
设路径为 \(\text{path}(u_i, v_i, w'_i)\),将考虑 \(p\) 的儿子 \(q\) 的方向的转移;若 \(u_i\) 或 \(v_i\) 是 \(q\) 的后代 (不妨设此处 \(u_i\) 是 \(q\) 的后代),则将 \(w'_i\) 插入 \(\text{dfn}_{u_i}\),对 \(q\) 的后代 \(s\) 及其儿子 \(t\),从 \(s\) 转移到 \(t\) 时查询"是 \(s\) 的后代且不是 \(t\) 的后代"的 \(\text{dfn}\) 区间的 max 即可
形式化的,设 \(i\) 的子树的 \(\text{dfn}\) 区间为 \([\text{dfn}_i, \text{bottom}_i]\),查询 \([\text{dfn}_s, \text{dfn}_t-1] \bigcup [\text{bottom}_t+1, \text{bottom}_s]\) 中的 max 即可
那么只需支持单点改 + 区间查 max 的线段树即可
实现时千万注意顺序,先求出所有儿子 \(v\) 的 \(g_v\),再将挂在 \(u\) 上的路径插入线段树,再递归求解 \(v\) 的子树
-
若 \(u\) 被形如 "\(u\) 的后代 \(\rightarrow u \rightarrow u\) 的后代" 方向的路径占用,同理令这样的路径的权的 max 为 \(w'\),有 \(g_v \leftarrow w'-f_v\)
设路径为 \(\text{path}(u_i, v_i, w'_i)\),显然有 \(\text{lca}(u_i, v_i) = u\),换句话说,这些路径都是挂在 \(u\) 上的
此时直接将这些路径排序,找到第一条满足 \(u_i\) 不是 \(v\) 的后代且 \(v_i\) 也不是 \(v\) 的后代的路径即可
每条路径最多浪费两次访问 (\(u_i\) 是 \(v\) 的后代 / \(v_i\) 是 \(v\) 的后代),时间复杂度没问题
现在考虑如何求 \(f(u, v)\) (这里只看 \(W(S)\) 减去的东西)
对路径 \(\text{path}(u, v)\),非 lca 的每个点的每个儿子的子树 (不包含路径上的点) 被分离出来;同时,lca 子树的补也会被分离出来
简单容斥下,有:
- 对不为 lca 的点 \(u\),贡献为 \(s_u-f_u\)
- 对为 lca 的点 \(u\),贡献为 \(g_u+s_u\)
求这个应当是简单的
时间复杂度 \(O(n \log n)\)
7.18
CF566C - Logistical Questions (观察性质 + 点分治 - 移动重心)
7.19
P8250 交友问题 (图上根号分治)
CF2101E - Kia Bakes a Cake (点分治优化 DP)
CF1270F - Awesome Substrings (根号分治)
7.20
开摆 + 补 whk
7.21
P4168 [Violet] 蒲公英 (分块 - 维护块间答案)
P3203 [HNOI2010] 弹飞绵羊 (分块 - 维护跳出块信息)
P10590 磁力块 (分块 - 维护块内指针)
CF455D - Serega and Fun (分块 - 维护 deque)
7.22
P2500 [SDOI2012] 集合 (图上根号分治)
7.23
P1494 [国家集训队] 小 Z 的袜子 (莫队)
CF852I - Dating (树上莫队)
\(O(n \log n) - O(1)\) lca
7.24
CF940F - Machine Learning (带修莫队)
7.25
P5903 【模板】树上 K 级祖先 (长剖求 K 级祖先)
P3591 [POI 2015] ODW (长剖求 K 级祖先 + 根号分治)
CF1491H - Yuezheng Ling and Dynamic Tree (分块 - 弹飞绵羊 Trick + 均摊)
7.26
讲群论 + 调不出来题
7.27
P11527 [THUPC 2025 初赛] waht 先生的法阵 (分块 - 弹飞绵羊 Trick + 均摊)
P2481 [SDOI2010] 代码拍卖会 (数位 DP - 前后缀拆分 Trick + 循环节)
CF708C - Centroids (换根 DP)
P6419 [COCI 2014/2015 #1] Kamp (换根 DP)
7.28
P3647 [APIO2014] 连珠线 (观察性质 + 换根 DP)
不妨先令点 \(1\) 为根;容易发现,只存在 \(u - v - v'\) ( \(v \in \text{son}_u, v' \in \text{son}_v\) ) 与 \(v_1 - u - v_2\) ( \(v_1, v_2 \in \text{son}_u\) ) 两种连蓝链的结构
由题意,两条蓝链不能在非端点处相交;结构 \(1\) 中 \(u\) 可作为端点,结构 \(2\) 中 \(u\) 不能作为端点,这会给 DP 带来很大麻烦
考虑观察性质;红线只能连接新珠子与已有珠子,而对于结构 \(2\),\(u\) 一定是已有珠子;因此有:
- 不存在两个结构 \(2\),使得 \(u_1, u_2\) 无祖先后代关系
那么我们必然可以找到一个点 \(rt\),使得以点 \(rt\) 为根时,所有结构 \(2\) 都变成结构 \(1\)
这启示我们进行换根 DP
先考虑子树内;令 \(f_{u, 0/1}\) 表示仅考虑 \(u\) 子树,\(u\) 所在蓝链长度为偶 / 奇的答案;转移为:
-
\(\displaystyle f_{u, 0} \leftarrow \sum_{v \in \text{son}_u} \max(f_{v, 0}, f_{v, 1}+w(u, v))\),表示每个儿子可以选择是否拼上点 \(u\)
-
\(\displaystyle f_{u, 1} \leftarrow f_{u, 0} + \max_{v \in \text{son}_u} (f_{v, 0} + w(u, v) - \max(f_{v, 0}, f_{v, 1}+w(u, v)))\),表示强行钦定一个儿子拼上点 \(u\)
为辅助换根,此处套路地记录 \(\max\) 的最大值 \(s_{u, 1}\) 与次大值 \(s_{u, 0}\)
初始值为对叶子 \(u\),\(f_{u, 0} = 0\),\(f_{u, 1} = -\infty\)
再令 \(dp_{u, v, 0/1}\) 表示从 \(u\) 子树中挖去 \(v\) 子树 (\(v \in \text{son}_u\)),\(u\) 所在蓝链长度为偶 / 奇的代价:
-
\(\displaystyle dp_{u, v, 0} \leftarrow f_{u, 0}-\max(f_{v, 0}, f_{v, 1}+w(u, v))\)
-
\(\displaystyle dp_{u, v, 1} \leftarrow dp_{u, v, 0}+s_{u, 0}\) [\(f_{u, 1}\) 的 \(\max\) 在 \(v\) 处取得]
\(\displaystyle dp_{u, v, 1} \leftarrow dp_{u, v, 0}+s_{u, 1}\) [\(f_{u, 1}\) 的 \(\max\) 不在 \(v\) 处取得]
再考虑子树外;令 \(g_{u, 0/1}\) 表示挖去 \(u\) 的子树 (保留点 \(u\)),\(u\) 所在蓝链长度为偶 / 奇的答案;转移为:
-
\(\displaystyle g_{v, 0} \leftarrow \max(g_{u, 0}+dp_{u, v, 0}, \max(g_{u, 1}+dp_{u, v, 0}, g_{u, 0}+dp_{u, v, 1})+w(u, v))\)
其中,\(g_{u, 0}+dp_{u, v, 0}\) 表示将点 \(v\) 单独空出来
\(\max(g_{u, 1}+dp_{u, v, 0}, g_{u, 0}+dp_{u, v, 1})+w(u, v)\) 表示钦定 \(u\) 所在蓝链长为奇,再拼上点 \(v\) 断成两条链
-
\(\displaystyle g_{v, 1} \leftarrow g_{u, 0}+dp_{u, v, 0}+w(u, v)\),表示将点 \(v\) 拼到子树外长度为偶的蓝链上
初始值为 \(g_{1, 0} = 0, g_{1, 1} = -\infty\);最终答案即为 \(\max (f_{u, 0}+g_{u, 0}, f_{u, 1}+g_{u, 1})\)
时间复杂度 \(O(n)\)
P1453 城市环路 (基环树 DP)
P4381 [IOI 2008] Island (基环树 DP)
P2495 [SDOI2011] 消耗战 (虚树 DP)
7.29
P3233 [HNOI2014] 世界树 (虚树 DP)
CF1009F - Dominant Indices (长链剖分优化 DP)
P5904 [POI 2014] HOT-Hotels 加强版 (神仙树形 DP + 长链剖分优化 DP)
P2627 [USACO11OPEN] Mowing the Lawn G (单调队列优化 DP)
P2569 [SCOI2010] 股票交易 (单调队列优化 DP)
7.30
CF939F - Cutlet (设计状态 + 单调队列优化 DP)
P1912 [NOI2009] 诗人小G (决策单调性优化 DP - 二分队列)
CF868F - Yet Another Minimization Problem (决策单调性优化 DP - 分治 + 类莫队暴力)
P3120 [USACO15FEB] Cow Hopscotch G (类 CDQ 分治优化 DP)
P3810 【模板】三维偏序(陌上花开) (CDQ 分治)
7.31
CF449D - Jzzhu and Numbers (SOS DP + 容斥)
P6442 [COCI 2011/2012 #6] KOŠARE (SOS DP + 容斥)
P3287 [SCOI2014] 方伯伯的玉米田 (观察性质 + 二维树状数组优化 DP)
CF53E - Dead Ends (状压 DP + 钦定转移顺序)
CF1781F - Bracket Insertion (括号序列转 \(\pm\) 1 + 分析操作 + 合并同类项优化 DP)
8.1
AGC013E - Placing Squares (组合意义 - 平方变放两个球的方案数 + 矩阵快速幂优化 DP)
直接 DP 没有前途;考虑用组合意义刻画平方与连乘
有 \(n\) 个格子,你可以在其中插入若干隔板;有 \(m\) 个特殊格 \(x_1, x_2, \cdots, x_m\),你不能在 \(x_{i}, x_{i}+1\) 格间插入隔板
现在对于每个格子连续段,你需要选两个格子 (可重) 放入一个黑球一个白球,求总方案数
这样转化的好处是,每个连续段中只有 \(2\) 个球,按照这一性质设计状态更易于以后进行优化
令 \(f_{i, j}\) 表示当前在第 \(i\) 格,当前格到上个隔板间放了 \(j\) 个球的总方案数 ( \(j \in \{0, 1, 2\}\) )
若 \(i-1\) 不为特殊格,则有转移:
- \(f_{i, 0} = f_{i-1, 0}+f_{i-1, 2}\) (不放隔板 / 放隔板)
- \(f_{i, 1} = 2f_{i-1, 0}+f_{i-1, 1}+2f_{i-1, 2}\) (放黑或白球 / 不放球 / 放隔板+放黑或白球)
- \(f_{i, 2} = f_{i-1, 0}+f_{i-1, 1}+2f_{i-1, 2}\) (放黑白球 / 放另一个球 / 不放隔板或放隔板+放黑白球)
同理,对特殊格,可得转移:
- \(f_{i, 0} = f_{i-1, 0}\)
- \(f_{i, 1} = 2f_{i-1, 0}+f_{i-1, 1}\)
- \(f_{i, 2} = f_{i-1, 0}+f_{i-1, 1}+f_{i-1, 2}\)
初始值为 \(f_{0, 0} = 1\),答案即为 \(f_{n, 2}\)
对每个不包含特殊格的连续段矩阵快速幂优化即可;时间复杂度 \(O(m \log n)\)
CF1174E - Ehab and the Expected GCD Problem (观察性质 + DP)
CF1765C - Card Guessing (拆贡献 + 概率 DP 转计数 DP)
由于每次贡献为 \(1\),可先将猜对次数的期望转化为每次猜对的概率之和
由于每次猜花色是本质不同的决策,此处认为同种花色的牌也是本质不同的牌
设当前考虑第 \(i\) 张牌,令 \(K = \min(k, i-1)\),显然我们只关心前 \(K\) 张牌中,出现次数最少的花色还剩多少张牌
套路地将概率 DP 转化为计数 DP;不妨设 \(g_{i, j}\) 表示共 \(i\) 张牌,出现次数最少的花色出现 \(j\) 次的方案数 (显然有多种花色出现次数最少不影响)
直接转移似乎不好做,不过注意到花色只有 \(4\) 种,考虑用背包解决
令 \(f_{i, j, k}\) 表示考虑了前 \(i\) 种花色,总共选了 \(j\) 张牌,出现次数最少的花色出现 \(k\) 次的方案数
显然有转移 \(f_{i, j, k} \rightarrow f_{i+1, j+c, \min(k, c)} \cdot \dbinom{j+c}{c} \cdot \dbinom{n}{c} \cdot c!\) (分配插入位置,分配牌,分配顺序)
初始值为 \(f_{1, i, i} = \dbinom{n}{i} \cdot i!\);最终,我们有 \(g_{i, j} = f_{4, i, j}\)
令 \(h_i\) 表示选 \(i\) 张牌的方案数,显然有 \(h_i = \sum_{j=0}^{n} g_{i, j}\)
那么对于第 \(i\) 位,贡献为 \(\displaystyle \frac{\sum_{j=0}^{n} g_{K, j} \cdot (n-j)}{h_{K+1}}\);特别的,当 \(i=1\) 时,贡献为 \(\displaystyle \frac{1}{4}\)
注意,这里前 \(K\) 张牌再以前的牌的选择方案上下抵消了,故不考虑
那么最终答案即为 \(\displaystyle \frac{1}{4} + \sum_{i=2}^{4n} \frac{\sum_{j=0}^{n} g_{K, j} \cdot (n-j)}{h_{K+1}}\)
时间复杂度 \(O(n^3)\)
CF1726E - Almost Perfect (观察置换环性质 + 简单部分 DP 复杂部分枚举组合)
CF1842G - Tenzing and Random Operations (组合意义 - 连乘变乘法原理 + DP)
8.2
CF1750F - Majority (正难则反 + 前缀和优化类连续段 DP)
8.3
ABC262Ex - Max Limited Sequence (转化限制 + 线段树优化 DP)
首先,我们可以用数据结构求出每个位置的最紧上界 \(u_i\)
显然,无解当且仅当存在限制 \([l, r, x]\),使得 \(\max (u_l, u_{l+1}, \cdots, u_r) < x\)
注意到对每个位置,实际上我们只关心该位置是否取到上界
显然每个上界相互独立,考虑对每个上界分别计算方案数后乘起来 (对没有限制的位置,方案数为 \(m+1\) )
设当前上界为 \(c\);取出所有 \(u_i = c\) 的位置 \(i\) 排成一个序列,取出所有 \(X_j = c\) 的限制 \(j\)
注意到对于每个限制 \(j\),其本质上是要求序列中一段下标区间内有至少 \(1\) 个位置取到上界
这启发我们记录序列中取到上界的位置;令 \(f_{i, j}\) 表示当前在第 \(i\) 位,上一个取到上界的位置为 \(j\) 的方案数
先不考虑限制,显然有转移:
- \(f_{i, i} \leftarrow f_{i-1, j}\),表示位置 \(i\) 取到上界
- \(f_{i, j} \leftarrow f_{i-1, j} \times c\),其中 \(i \ne j\),表示位置 \(i\) 未取到上界
对于每个限制,我们将其挂在右端点考虑;具体的,对于限制 \([l, r]\),在 \(i=r\) 时清空所有 \(j < l\) 的 \(f_{i, j}\)
初始值为 \(f_{0, 0} = 1\),答案为 \(\sum_{j} f_{\text{len}, j}\)
这显然可以通过支持单点改 + 区间乘 + 区间推平 + 全局和的线段树维护
时间复杂度 \(O(n \log n)\)
AGC030F - Permutation and Minimum (转化限制 + DP)
先转化下题意
将 \(1, 2, \cdots, 2n\) 排成一排,从左向右连线匹配,匹配值为左侧数的值,求方案数
记 \(v_u = 0/1\) 表示 \(u\) 在 \(a_i\) 中不存在 / 存在,对匹配 \((x, y)\) ( \(x < y\) ),分类讨论:
- \(v_x = 1, v_y = 1\),此时匹配位置和值均确定,直接扔掉即可
- \(v_x = 1\) 或 \(v_y = 1\),此时匹配位置确定
- \(v_x = 0, v_y = 0\),此时可以自由分配匹配位置
注意到不好用 DP 刻画 "自由分配匹配位置",不过你发现第三类匹配的个数确定,考虑先转化为无序,最后乘阶乘定序
因此,两种匹配方案不同,当且仅当:
- 匹配值不同,即存在位置 \(i\) 在第一种方案中是左端点,第二种方案中不是
- 匹配位置不同,即存在匹配 \((i, x), (i, y)\) 使得 \(x \ne y\) 且 \(v_x = 1, v_y = 1\)
考虑从大向小 DP,逐个确定匹配位置,记 \(f_{i, j, k}\) 表示从 \(2n\) 填到 \(i\),有 \(j\) 个 \(v_x = 0\) 的右端点,有 \(k\) 个 \(v_x = 1\) 的右端点
易得转移:
- \(v_i = 0\) 时:
- \(f_{i, j, k} \leftarrow f_{i+1, j+1, k}\),表示匹配一个 \(v_x = 0\) 的右端点
- \(f_{i, j, k} \leftarrow f_{i+1, j, k+1} \times (k+1)\),表示匹配一个 \(v_x = 1\) 的右端点,需要考虑匹配位置
- \(f_{i, j, k} \leftarrow f_{i+1, j-1, k}\),表示不匹配
- \(v_i = 1\) 时:
- \(f_{i, j, k} \leftarrow f_{i+1, j+1, k} \times (j+1)\),表示匹配一个 \(v_x = 0\) 的右端点
- \(f_{i, j, k} \leftarrow f_{i+1, j, k-1}\),表示不匹配
初始值为 \(f_{2n+1, 0, 0} = 1\),答案为 \(f_{1, 0, 0}\)
时间复杂度 \(O(n^3)\)
8.4
ARC106E - Medals (Hall 定理 + SOS DP)
CF21D - Traveling Graph (欧拉回路状压 DP)
8.5
CF1773G - Game of Questions (观察性质 - 去除无用状态 + 状压 DP)
注意到 \(m \le 17\),考虑对人状压一下
不妨令 \(f_S\) 表示场上剩余的人组成的集合为 \(S\) 时,Genie 的获胜概率
若转移时直接枚举问题,有可能会选重,记在状态里又接受不了;但注意到,重复选择问题不会使 \(S\) 变化,因此无需考虑这件事
具体的,设有 \(c_0\) 种问题可以使 \(S\) 减少,\(c_1\) 种问题可以使 \(S\) 变成 \(T\) ( $ T \varsubsetneqq S$ ),即有 \(\displaystyle f_S \leftarrow f_T \times \frac{c_1}{c_0}\) (只在 \(S\) 产生变化时统计)
直接 \(O(n2^m)\) 枚举问题显然无法接受,考虑预处理 \(g_{S, T}\) ( \(T \subset S\) ) 表示使 \(S\) 变成 \(T\) 的问题数量
对 \(g_{S, T}\) 的转移,考虑钦定一个 \(x (x \notin S)\),则所有使 \(S\) 变成 \(T\) 的问题可分为同时 ban 掉 \(x\) 与不同时 ban 掉 \(x\) 两类
显然有转移 \(g_{S, T} \leftarrow g_{S \cup \{x\}, T} + g_{S \cup \{x\}, T \cup \{x\}}\)
对 \(f_S\),则有转移 \(\displaystyle f_S \leftarrow \frac{\sum_{T \varsubsetneqq S} f_{T} g_{S, T}}{n-g_{S, \varnothing}-g_{S, S}}\)
初始值为对所有 \(g_{S, \varnothing}+g_{S, S} = n\) 的 \(S\),\(f_{S}= [1 \in S]\);答案即为 \(f_{\{1, 2, \cdots, m\}}\)
时间复杂度为 \(O(nm + 3^m)\)
P9129 - [USACO23FEB] Piling Papers G (区间 DP 套类数位 DP)
8.6 - 8.9
准备讲课 PPT + 补 whk
8.10 - 8.16
军训
8.17
ARC103F - Distance Sums (观察性质 + 类拓扑思想)
CF1528C - Trees of Tranquillity (虚树思想贪心)
CF842E - Nikita and game (树的直径性质 - 交于一点或一边 + 维护左右集合)
CF2108E - Spruce Dispute (拆贡献 + 贪心 - 最大子树最小取重心)
CF1783G - Weighed Tree Radius (补齐对称性 + 观察性质 + 线段树维护区间直径)
AGC018D - Tree and Hamilton Path (拆贡献 + 贪心 - 最大子树最小取重心 + 回路减边变路径)
8.18
8.18 T1 - Rainbow 的信号 (拆位 + 拆贡献)
8.18 T2 - Freda 的传呼机 (仙人掌最短路)
8.18 T4 - Tree (分数规划 + 树形 DP)
8.19
8.18 T3 - Circle (观察性质 + 类数位 DP)
P12205 [COI 2022] 通行证件 / Vinjete (线段树维护区间 > 0 个数)
8.20
P6348 [PA 2011] Journeys (线段树优化建图)
ABC414G - AtCoder Express 4 (线段树优化建图 - 左右端点差分)
P6453 [COCI 2008/2009 #4] PERIODNI (笛卡尔树上 DP)
P3809 【模板】后缀排序 ( \(O(n \log n)\) SA)
令 \(\text{sa}_i\) 表示排名为 \(i\) 的后缀位置,\(\text{rk}_i\) 为后缀 \(\text{suf}_i\) 的排名
做法 1:\(O(n \log^2 n)\)
考虑直接对每个后缀排序,这样是 \(O(n^2 \log n)\) 的
考虑优化比较;二分哈希求 \(\text{lcp}\) 后比较下一位即可,这样单次比较 \(O(\log n)\),总时间复杂度 \(O(n \log^2 n)\)
做法 2:\(O(n \log^2 n)\)
考虑倍增的思想求解
令 \(\text{rk}_{i}^{j}\) 表示考虑所有长为 \(2^j\) 的子串 (不足补极小值),从 \(i\) 开始的排名,\(\text{sa}_{i}^{j}\) 类似
初始时,我们对每个字符排序可得 \(\text{rk}_{i}^{1}\) 与 \(\text{sa}_{i}^{1}\)
从 \(j\) 倍增到 \(j+1\) 时,对所有位置 \(i\) 以 \(\text{rk}_{i}^{j}\) 以第 \(1\) 关键字、\(\text{rk}_{i+2^j}^{j}\) 以第 \(2\) 关键字排序即可
注意,需要特别处理 \(\text{rk}_{p}^{j} = \text{rk}_{q}^{j}\) 且 \(\text{rk}_{p+2^j}^{j} = \text{rk}_{q+2^j}^{j}\) 的情况,此时 \(\text{rk}_{p}^{j+1} = \text{rk}_{q}^{j+1}\);排好序后重新扫一遍即可
只需做到 \(2^j \ge n\),共 \(O(\log n)\) 轮,总时间复杂度 \(O(n \log^2 n)\),瓶颈在排序
做法 3:\(O(n \log n)\)
做法 2 的瓶颈在排序,用基数排序 (做两次计数排序) 优化即可
实现上的小细节:
-
第 \(1\) 次排序无需考虑第 \(2\) 关键字
-
对第 \(2\) 关键字排序无需计数排序:
for (int i=n-w+1; i<=n; i++) tot++, id[tot] = i(处理 \(i+w > n\) 的情况,此时没有先后顺序)for (int i=1; i<=n; i++) if (sa[i] > w) tot++, id[tot] = sa[i]-w(直接处理 \(i+w\) 的顺序) -
排好序后需要重新扫一遍处理 \(\text{rk}\) 相同的情况
-
可动态令基数排序的值域 \(V = \max\{\text{rk}_i\}\)
-
\(\max\{\text{rk}_i\} \ge n\) 时无需继续排序,直接
break即可
令 \(\text{h}_i = \text{lcp}(\text{suf}_{\text{sa}_{i-1}}, \text{suf}_{\text{sa}_{i}})\),考虑如何求 \(\text{h}_i\)
结论 1:\(\text{h}_{\text{rk}_i} \ge \text{h}_{\text{rk}_{i-1}}-1\)
当 \(\text{h}_{\text{rk}_{i-1}} \le 1\) 时,显然成立
当 \(\text{h}_{\text{rk}_{i-1}} > 1\) 时,令旧 \(\text{lcp}\) 为 \(\text{aA}\),其中 \(\text{a}\) 为字符,\(\text{A}\) 为串;因此,令 \(\text{suf}_{i-1}\) 为 \(\text{aAD}\),另一个为 \(\text{aAB}\)
因此,我们有 \(\text{suf}_i\) 为 \(\text{AD}\),且存在 \(\text{AB}\);由于 \(\text{rk}_i-1\) 比 \(\text{rk}_i\) 仅小 \(1\),因此有 \(AB \le \text{suf}_{\text{sa}_{\text{rk}_i-1}} < AD\)
综上,新 \(\text{lcp}\) 至少为 \(\text{A}\),证毕
我们可以根据这一性质暴力求 \(\text{h}_i\) (初始值 \(\text{h}_1 = 0\) )
结论 2:\(\displaystyle \text{lcp}(\text{suf}_i, \text{suf}_j) = \min_{\text{rk}_i+1 \le k \le \text{rk}_j} \text{h}_k\)
显然,必有 \(\displaystyle \text{lcp}(\text{suf}_i, \text{suf}_j) \ge \min_{\text{rk}_i+1 \le k \le \text{rk}_j} \text{h}_k\)
感性理解上,由于我们按字典序枚举,\(\text{lcp}\) 只要掉下去就回不来了,成立
8.21
AT_ddcc2020_final_c - Smaller-Suffix-Free Sequences (SA)
题意:给定串 \(s\),对其每个后缀,求出该后缀的一个最长前缀,满足这个前缀为 Lyndon 串
一个串称为 Lyndon 串,当且仅当其字典序严格小于其所有后缀;\(1 \le |s| \le 2 \times 10^5\)
令原串为 \(s\),\(s\) 的一个后缀的前缀为 \(t\),\(t\) 的一个后缀为 \(t'\)
容易发现,\(t < t'\) 与 \(t+x < t'+x\) 是等价的;那么我们只需判断原串后缀与后缀的字典序关系即可
这启示我们建出 SA;则原题可转化为:
- 对每个 \(1 \le i \le n\),求出最大的 \(j\) 使得 \(\displaystyle \text{rk}_i = \min_{i \le p \le j} \text{rk}_p\)
单调栈维护即可;时间复杂度 \(O(n \log n)\),瓶颈在建 SA
P2178 [NOI2015] 品酒大会 (SA - 并查集维护 \(h_i\) )
由于有经典结论 \(\displaystyle \text{lcp}(\text{suf}_i, \text{suf}_j) = \min_{\text{rk}_i+1 \le k \le \text{rk}_j} \text{h}_k\),考虑建出 SA
考虑从大到小加入 \(\text{h}_{\text{rk}_i}\),每个 \(\text{h}_{\text{rk}_i}\) 放在 \(\text{rk}_i\) 位置上,过程中会形成许多连续段;考虑用并查集维护
具体的,对于连续段 \([l, r]\),其对应满足 \(l-1 \le \text{rk}_i \le r-1\) 且 \(l \le \text{rk}_j \le r\) ( \(i < j\) ) 的 \(\text{lcp}(\text{suf}_i, \text{suf}_j)\)
对第一问,连续段 \([l, r]\) 对应的选择方案数为:
- \(l = 1\),则为 \(\displaystyle 1+2+\cdots+(r-1) = \frac{1}{2} r(r-1)\)
- \(l \ne 1\),则为 \(\displaystyle 1+2+\cdots+(r-l+1) = \frac{1}{2} (r-l+2)(r-l+1)\)
这启示我们维护连续段的左右端点,合并时减去两个小区间贡献再加上新区间贡献即可
对第二问,合并 \([l_1, r_1], [l_2, r_2]\) 时 (不妨设 \(l_2 > r_1\) ):
-
左端点取在 \([\max(1, l_1-1), r_1-1]\),右端点取在 \([l_1, r_1]\),已经在计算 \([l_1, r_1]\) 的答案时贡献过
-
左端点取在 \([\max(1, l_2-1), r_2-1]\),右端点取在 \([l_2, r_2]\),已经在计算 \([l_2, r_2]\) 的答案时贡献过
-
左端点取在 \([\max(1, l_1-1), r_1-1]\),右端点取在 \([l_2, r_2]\)
这启示我们对区间 \([l, r]\) 维护 \(a\) 上 \([\text{sa}_l, \text{sa}_{r-1}]\) 的最大 / 次大 / 最小 / 次小值
合并时,我们:
- 用 ( \([l_1, r_1]\) 的相关值 + \(a_{\text{sa}_{l_1-1}} [l_1 \ne 1]\) ) 与 ( \([l_2, r_2]\) 的相关值 + \(a_{\text{sa}_{r_2}}\) ) 更新 \([l_1, r_2]\) 答案
- 用 ( \([l_1, r_1]\) 的相关值 + \(a_{\text{sa}_{r_1}}\) ) 与 ( \([l_2, r_2]\) 的相关值 ) 更新 \([l_1, r_2]\) 相关值
- 根据实现不同,当 \(l_1 = r_1\) 时可能需要特殊处理 (如补上 \(a_{\text{sa}_{l_1}}\) )
时间复杂度 \(O(n \log n)\),瓶颈在建 SA
8.22
P9664 [ICPC 2021 Macao R] LCS Spanning Tree (SA - 只需考虑 sa 相邻的 lcs)
首先考虑只有两个串 \(s_i, s_j\) 怎么做
\(\text{lcs}\) 自然启发我们建 SA,这启示我们将 \(s_i, s_j\) 拼接起来,在 \(s_i\) 中选取左端点 \(l\),在 \(s_j\) 中选取右端点 \(r\) 求 \(\text{lcp}\)
此时出现了两个问题:
-
\(\text{lcp}(\text{suf}_l, \text{suf}_r) \ge |s_i|-l+1\),即 \(\text{lcp}\) 超出 \(s_i\)
这可以通过在 \(s_i, s_j\) 间添加特殊连接符规避
-
暴力选取 \(l, r\) 在时间复杂度上无法接受
注意到只有排名相邻的后缀是有用的,枚举排名 \(i\),贡献为 \(\text{lcp}(\text{suf}_{\text{sa}_{i-1}}, \text{suf}_{\text{sa}_i}) = \text{h}_i\)
综上,判掉 \(\text{sa}_{i-1}\) 与 \(sa_i\) 在一个串中的情况,连边 \((\text{sa}_{i-1}, \text{sa}_i, \text{h}_i)\) 即可
多个串没有本质区别,全部拼起来,中间加上不同的连接符即可
时间复杂度 \(O(n \log n)\)
CF547E - Mike and Friends (SA + 二维数点)
省流:我觉得我真想不清楚细节,所以写的会繁琐亿点
类比上题,套路地将 \(s_1, s_2, \cdots, s_n\) 拼成新串,中间加上不同的分割符;记新串中 \(s_i\) 的对应位置为 \([\text{st}_i, \text{ed}_i]\)
对于查询 \((l, r, k)\),对满足条件的新串位置 \(i\),考虑分讨限制条件:
- 显然要求 \(i \in [\text{st}_l, \text{ed}_r]\)
- 令 \(p = \text{st}_k\),若 \(\text{rk}_i < \text{rk}_{p}\),则有 \(\displaystyle \text{lcp}(\text{suf}_i, \text{suf}_p) = \min_{\text{rk}_i+1 \le q \le \text{rk}_p} \text{h}_q = |s_k|\)
- 若 \(\text{rk}_i = \text{rk}_{p}\),事实上即 \(i = p\),显然成立
- 若 \(\text{rk}_i > \text{rk}_{p}\),则有 \(\displaystyle \text{lcp}(\text{suf}_i, \text{suf}_p) = \min_{\text{rk}_p+1 \le q \le \text{rk}_i} \text{h}_q = |s_k|\)
这启示我们对每个位置 \(i\) 维护:
- 最靠前的 \(l_i\),满足 \(\displaystyle \min_{l \le j \le \text{rk}_i} \text{h}_{j} = \text{h}_{\text{rk}_i}\)
- 最靠后的 \(r_i\),满足 \(\displaystyle \min_{\text{rk}_{i+1} \le j \le r} \text{h}_{j} = \text{h}_{\text{rk}_{i+1}}\)
这里直接二分维护即可
回到询问 \((l, r, k)\),对满足条件的新串位置 \(i\),考虑转化限制条件:
- \(i \in [\text{st}_l, \text{ed}_r]\)
- \(\text{rk}_i\) 也需在一个区间 \([l', r']\) 内,令 \(p = \text{st}_k\),具体的:
- 若 \(\text{h}_{\text{rk}_p} = |s_k|\),则左半区间成立,有 \(l' = l_k\);反之 \(l' = \text{rk}_p\)
- 若 \(\text{h}_{\text{rk}_{p+1}} = |s_k|\),则右半区间成立,有 \(r' = r_k\);反之 \(r' = \text{rk}_p\)
那么直接二维数点即可;时间复杂度 \(O(n \log n)\)
8.23
CF1923F - Shrink-Reverse (观察性质 + SA)
首先考虑没有翻转操作怎么做
我们每次必然贪心地将最靠前的 \(1\) 与最靠后的 \(0\) 交换;双指针维护即可,这部分时间复杂度 \(O(n)\)
考虑观察翻转操作的性质
显然,\(1\) 次翻转可以去前导零,\(2\) 次可以去后导零,\(\ge 3\) 次是没有意义的
对于 \(2\) 次翻转,我们发现去后导零并没有改变答案;若翻转后再交换,显然可以不劣地等效为先交换再翻转
综上,只需考虑 \(1\) 次翻转的情况,且这次翻转必然最后进行
类比没有翻转的情况,一个 naive 的想法是将最靠后的 \(1\) 与前导零的末尾交换
如 \(00101100\) 的情况就可以 hack 掉这个做法,因为我们可以与中间的 \(0\) 交换以缩小长度
这启示我们先确定最小长度 \(L\);显然先翻转 \(s\) 不影响答案,以下的 \(s\) 均指翻转后的串
对每个 \(i\),考虑求出最靠前的 \(r_i\) 使得可以将所有 \(1\) 换到 \([i, r_i]\) 中:
-
记 \([i, r_i]\) 中 \(0\) 的个数为 \(c_0\),\([i, r_i]\) 外 \(1\) 的个数为 \(c_1\)
则 \([i, r_i]\) 合法等价于 \(c_1 \le k-1\) (能换进来) 且 \(c_1 \le c_0\) (有地方容纳)
\(i\) 增大时,\(c_0\) 不增,\(c_1\) 不降,\(r_i\) 也是单调的;于是这部分也可以双指针做到 \(O(n)\)
接下来考虑最小化字典序;显然换进来的 \(1\) 会优先向后放,比的实际上是后缀的一段前缀的字典序
这启示我们跑 SA,按 \(\text{rk}\) 从小到大枚举,第一个合法的长度为 \(L\) 的前缀就是答案;证明:
-
若 \(\text{lcp} > L\),由于外面的 \(1\) 全部被换进来,已经完全相同,前后顺序没有影响
-
若 \(\text{lcp} \le L\) 且换进来的 \(1\) 没有影响 \(\text{lcp}\),直接按 \(\text{rk}\) 排就是对的
若 \(\text{lcp} \le L\) 且换进来的 \(1\) 影响了 \(\text{lcp}\),由于里面 \(1\) 的总数确定,一定是将一段后缀部填成 \(1\),前后顺序没有影响
时间复杂度 \(O(n \log n)\),瓶颈在建 SA
8.24
P4094 [HEOI2016/TJOI2016] 字符串 (SA + 二分答案 + 主席树 - 区间查非严格前驱 / 后缀)
子串 \(\text{lcp}\) 自然启示我们建 SA,同时把子串拼成后缀考虑
对于查询 \((a, b, c, d)\),其答案为:
-
\(\displaystyle \max_{a \le p \le b} \{\min\{\text{lcp}(\text{suf}_p, \text{suf}_c),\ b-p+1,\ d-c+1\}\}\)
这个式子表示对后缀求 \(\text{lcp}\) 后限制不能超出子串长度
限制不能超出 \([a, b]\) 子串长度的 \(b-p+1\) 一项很烦,考虑怎么去掉
考虑二分答案 \(\text{mid}\) 再刻画限制,提前确定合法区间 \([a, b-\text{mid}+1]\),这样就无需考虑这个问题
套路地,对于 \(a \le i \le b-\text{mid}+1\),我们对 \(\text{lcp}(\text{suf}_i, \text{suf}_c)\) 分类讨论:
- \(\text{rk}_i \le \text{rk}_c-1\),此时 \(\displaystyle \text{lcp}(\text{suf}_i, \text{suf}_c) = \min_{\text{rk}_i+1 \le p \le \text{rk}_c} \text{h}_p\),只需取 \(\le \text{rk}_c-1\) 的最大的 \(\text{rk}_i\) 计算
- \(\text{rk}_c+1 \le \text{rk}_i\),此时 \(\displaystyle \text{lcp}(\text{suf}_i, \text{suf}_c) = \min_{\text{rk}_c+1 \le p \le \text{rk}_i} \text{h}_p\),只需取 \(\ge \text{rk}_c+1\) 的最小的 \(\text{rk}_i\) 计算
- \(\text{rk}_i = \text{rk}_c\),此时 \(i = c\),答案即为 \(\min\{b-i+1, d-i+1\}\)
综上,我们需要支持区间查非严格前驱 / 后继
一个直观的想法是二分排名 + 静态区间第 \(k\) 小,上主席树,总时间复杂度 \(O(n \log^3 n)\)
事实上,可以发现二分排名是不必要的,设查询区间为 \([l, r]\):
- 若对 \(x\) 求前驱,线段树上二分查出 \(x\) 的排名,再对排名求值即可
- 若对 \(x\) 求后继,线段树上二分查出 \(x-1\) 的排名,再对排名 \(+1\) 求值即可
综上,我们维护 SA + 主席树 + 对 \(\text{h}_i\) 的 ST 表,总时间复杂度 \(O(n \log^2 n)\)
8.25
P4248 [AHOI2013] 差异
将式子拆成两部分,即 \(\displaystyle \sum_{1 \le i < j \le n} (|\text{suf}_i|+|\text{suf}_j|)\) 与 \(\displaystyle \sum_{1 \le i < j \le n} \text{lcp}(\text{suf}_i, \text{suf}_j)\)
前半部分易知为 \(\displaystyle \sum_{i=1}^{n-1} (\frac{i(i+1)}{2}+(n-i+1)(n-i))\),考虑如何求后半部分
\(i = j\) 的情况本身不合法无需统计,易得 \(\text{h}_i\) 上的每个区间 \([l, r]\) 都会贡献 \(\displaystyle \min_{l \le p \le r} \text{h}_p\)
对每个 \(i\),处理出:
- 最靠前的 \(\text{ls}_i\) 使得 \(\displaystyle \min_{\text{ls}_i \le p \le i}\text{h}_p = \text{h}_i\) 且在 \(\text{h}_i\) 处唯一取到
- 最靠后的 \(\text{rs}_i\) 使得 \(\displaystyle \min_{i \le p \le \text{rs}_i} \text{h}_p = \text{h}_i\)
在 \([\text{ls}_i, i]\) 中选左端点,\([i, \text{rs}_i]\) 中选右端点,贡献即为 \((i-\text{ls}_i+1) \times (\text{rs}_i-i+1) \times \text{h}_i\)
可二分 + ST 表或单调栈维护 \(\text{ls}_i\) 与 \(\text{rs}_i\),时间复杂度 \(O(n \log n)\)
8.26
P5161 WD与数列
对位差相等是困难的;注意到两序列增量相同,考虑对原序列差分
问题转化为:计数存在多少对 \([l_1, r_1], [l_2, r_2]\),使得 \(r_1 < l_2\) 且 \(a_{[l_1+1, r_1]} = a_{[l_2+1, r_2]}\) (注意第 \(1\) 位可以不同)
那么贡献分为两部分:
-
只有第 \(1\) 位,贡献为 \(\binom{n}{2}\)
-
扔掉第 \(1\) 位,计数存在多少对不交且不相邻的相等子串
注意,虽然此处视为 "扔掉",最后还是要往前拼 \(1\) 位的,子串左端点不能为 \(1\)
考虑处理不交且不相邻的相等子串对数
令靠前的子串开头为 \(i\),靠后的为 \(j\),考虑将 "不相邻" 刻画为对 \(j-i-1\) 取 \(\min\)
答案式子为 \(\displaystyle \sum_{1 < i < j \le n} \min\{\text{lcp}(\text{suf}_i, \text{suf}_j), j-i-1\}\)
考虑先求出 \(\displaystyle \sum_{1 < i < j \le n} \text{lcp}(\text{suf}_i, \text{suf}_j)\),再把 \(\ge j-i\) 的部分换成 \(j-i-1\)
前半部分是容易的,可以参考上道题
对于后半部分,考虑枚举 \(\text{len} = j-i\),使用 P1117 [NOI2016] 优秀的拆分 的 trick:
-
在 \(\text{len}, 2 \times \text{len}, 3 \times \text{len}, \cdots\) 处设关键点
-
对关键点 \(k\) 与 \(k+\text{len}\),求出 \(i \in [\max\{2, k-len+1\}, k]\) 的贡献
注意到这样处理不到 \(i\) 在倒数第二个关键点后的情况,不过此时必有 \(\text{lcp} < \text{len}\),没有贡献
-
求出 \(\text{lcp}(\text{suf}_k, \text{suf}_{k+\text{len}})\) 与 \(\text{lcs}(\text{pre}_k, \text{pre}_{k+\text{len}})\),则只需考虑 \(i \in [\max\{2, k-\min\{\text{len}, \text{lcs}\}+1\}, k]\)
此时 \(\text{lcp}\) 长度的贡献为等差数列,可以 \(O(1)\) 求出
使用 SA 维护原串与反串的后缀 \(\text{lcp}\) 即可
时间复杂度 \(O(n \log n)\)
8.27
P3804 【模板】后缀自动机(SAM)
SAM (后缀自动机),可以始终在 \(O(|s|)\) 的结点和边的代价下存储 \(s\) 的子串信息,将 \(s\) 的子串高度压缩以存储
下面给出一些定义:
-
\(\text{endpos}\):对 \(s\) 及其子串 \(t\),定义 \(t\) 的 \(\text{endpos}\) 集合为 \(s\) 中所有 \(t\) 出现的结束位置构成的集合
例:对
aabab,子串ab的 \(\text{endpos}\) 集合为 \(\{3, 5\}\)引理 1:对 \(s\) 及其子串 \(u, v (|v| \le |u|)\),\(u\) 与 \(v\) 的 \(\text{endpos}\) 集合相同 \(\iff v\) 在 \(s\) 中每次出现都是以 \(u\) 的后缀的形式
-
\(\text{endpos}\) 等价类:\(\text{endpos}\) 相同的所有子串构成的集合
例:对
aabab,子串ab与b同属一个 \(\text{endpos}\) 等价类引理 2:\(\text{endpos}\) 等价类中子串的长度连续,且所有子串均为最长子串的后缀
SAM 中的每个节点实际上代表一个 \(\text{endpos}\) 等价类
从初始节点 \(\text{rt}\) 到节点 \(u\) 会经过许多转移边,将转移边上的字符写下来形成字符串,这些字符串的 \(\text{endpos}\) 集合都相同
在 SAM 上的每个节点 \(u\),我们维护:
-
\(\text{len}\):当前 \(\text{endpos}\) 等价类中最长子串的长度
-
\(\delta(u, c)\):\(u\) 经过转移边 \(c\) 到达的节点
-
\(\text{link}\) (后缀链接):设当前 \(\text{endpos}\) 等价类中最长子串为 \(p\),最长的不属于当前 \(\text{endpos}\) 集合的 \(p\) 的后缀为 \(q\)
则 \(u\) 的后缀链接 \(\text{link}(u)\) 指向代表 \(q\) 所在 \(\text{endpos}\) 等价类的节点
引理 3:将所有 \(u\) 与 \(\text{link}(u)\) 连边,会形成一个树形结构
证明:显然只会从长串向短串连边,最终都连到初始节点 \(\text{rt}\),必然形成以 \(\text{rt}\) 为根的树
我们也将这一树形结构称为 \(\text{parent tree}\)
初始时,创建初始节点 \(\text{rt}\),代表空串;令 \(\text{len}(\text{rt}) = 0\) 且 \(\text{link}(\text{rt}) = -1\)
考虑增量构造 SAM,设原串为 \(s\),每次加入一个字符 \(c\) 变为 \(s+c\),创建新节点 \(u\) 代表当前插入状态
设 \(s+c\) 的末尾位置为 \(i\),则 \(u\) 实际代表的 \(\text{endpos}\) 集合为 \(\{i\}\)
设上次插入状态的对应节点为 \(v\),考虑维护 \(u\) 的 \(\text{len, link}\) 及其他节点向 \(u\) 的转移关系:
-
显然有 \(\text{len}(u) = \text{len}(v)+1\)
-
考虑尝试在 \(s\) 的每个后缀 \(\text{endpos}\) 等价类的末尾追加 \(c\) 形成 \(s+c\) 的后缀 \(\text{endpos}\) 等价类
我们从 \(v\) 开始跳 \(\text{link}\),设当前跳到的节点为 \(p\),对应 \(\text{endpos}\) 等价类中的最长子串为 \(t\):
-
若 \(\delta(p, c)\) 不存在,说明子串 \(t+c\) 在 \(s\) 中不存在,在 \(s+c\) 中的 \(\text{endpos}\) 集合就是 \(\{i\}\)
易知 \(t+c\) 与 \(s+c\) 同属一个 \(\text{endpos}\) 等价类,因此,令 \(\delta(p, c) = u\)
考虑一直跳 \(\text{link}\) 直到跳到 \(-1\) 或出现第一个满足 \(\delta(p, c)\) 存在的节点 \(p\):
-
若跳到 \(-1\),说明 \(c\) 这个字符在 \(s\) 中不存在,直接令 \(\text{link}(u) = \text{rt}\) 即可
-
若出现第一个满足 \(\delta(p, c)\) 存在的节点 \(p\),令 \(q = \delta(p, c)\)
-
若 \(\text{len}(q) = \text{len}(p)+1\),则 \(t+c\) 刚好为最长的 \(\text{endpos}\) 等价类不为 \(\{i\}\) 的后缀,令 \(\text{link}(u) = q\) 即可
-
需要注意,此时 \(q\) 代表的 \(\text{endpos}\) 等价类中的最长子串不一定为 \(p+c\),可能更长
例:对字符串
abb,插入最后一个b前 SAM 结构如下:- 节点 \(0\),\(\text{endpos}\) 等价类中为空串,\(\text{link} = -1\)
- 节点 \(1\),\(\text{endpos}\) 等价类中为
{a},\(\text{link} = 0\),连边 \(\delta(0, a) = 1\) - 节点 \(2\),\(\text{endpos}\) 等价类中为
{ab, b},\(\text{link} = 0\),连边 \(\delta(0, b) = 2, \delta(1, b) = 2\)
插入最后一个
b时,我们会跳 \(\text{link}\) 到 \(0\),此时 \(p = 0, q = \delta(0, b) = 2\)此时节点 \(2\) 代表的 \(\text{endpos}\) 等价类中除了
b,还有更长的ab以此例为例,我们发现节点 \(3\) (即新创建的 \(u\) ) 代表的 \(\text{endpos}\) 等价类应为
{abb, bb}此时节点 \(3\) 的 \(\text{link}\) 对应的 \(\text{endpos}\) 等价类应为
b;这启示我们将节点 \(2\) 进行 "分裂",变为ab与b -
综上,若 \(\text{len}(q) > \text{len}(p)+1\),考虑将节点 \(q\) 分裂出一个 \(q'\),后缀长度分界线即为 \(\text{len}(p)+1\)
令 \(q\) 代表的 \(\text{endpos}\) 集合为 \(S\),可视为新节点 \(q\) 仍维护 \(\text{endpos}\) 集合 \(S\),新节点 \(q'\) 维护 \(S \cup \{i\}\)
具体的,先克隆 \(q' = q\) (即储存信息全部相同),令:
- \(\text{len}(q') = \text{len}(p)+1\)
- \(\text{link}(u) = q'\)
- \(\text{link}(q) = q'\)
接下来从节点 \(p\) 继续跳 \(\text{link}\),将所有 \(\delta(p', c) = q\) 改为 \(\delta(p', c) = q'\) 即可
-
-
现在我们已经能够建出 SAM,回到本题
考虑暴力枚举每个节点 \(i\),将 \(\text{link}(i)\) 与 \(i\) 连边,建出 \(\text{parent tree}\)
记录每次插入时的节点 \(u\),将这些点赋权 \(1\),其他点赋权 \(0\) (实现时最好不要直接在 SAM 节点上记录权,复制时容易出错)
易知对于节点 \(i\),其代表的 \(\text{endpos}\) 集合的大小即为 \(i\) 子树内 \(1\) 的个数
对于一个 \(\text{endpos}\) 等价类,我们肯定取最长的子串,长度即为 \(\text{len}(i)\);那么 dfs 一遍取 \(\max\) 即可
若直接开数组存 \(\delta(u, c)\),时间复杂度 \(O(n)\),空间复杂度 \(O(n|\Sigma|)\)
若使用 \(\text{map}\) 存 \(\delta(u, c)\),时间复杂度 \(O(n \log |\Sigma|)\),空间复杂度 \(O(n)\)
SP1812 LCS2 - Longest Common Substring II (SAM - 两串求最长公共子串 (lcs))
看到求 \(\text{lcs}\),考虑建 SAM
先考虑两个串 \(s, t\) 怎么做
对 \(s\) 建 SAM,遍历 \(t\) 放到 SAM 中查询
设当前遍历到 \(t_i\);考虑维护 \(t_{[1, i]}\) 在 SAM 上的匹配位置 \(u\) 以及匹配长度 \(l\),设 \(t_{[1, i-1]}\) 的相关信息为 \(u'\) 与 \(l'\):
- 若 \(\delta(u', t_i)\) 存在,则令 \(u = \delta(u', t_i)\),\(l = l'+1\)
- 若 \(\delta(u', t_i)\) 不存在,考虑类似建 SAM 时插入字符的思想,跳 \(\text{link}\) 直到 \(\delta(u', t_i)\) 存在:
-
若跳完 \(\text{link}\) 也没有找到使 \(\delta(u', t_i)\) 存在的 \(u'\),令 \(u = \text{rt}\),\(l = 0\)
-
反之,令 \(u = \delta(u', t_i)\),\(l = \text{len}(u')+1\)
对任意节点 \(i\),都有 \(i\) 代表的 \(\text{endpos}\) 等价类中最短子串长度 \(>\) \(\text{len}(\text{link}(i))\)
因此,跳 \(\text{link}\) 时 \(\text{len}(u')+1\) 必然是能取到的
-
那么我们在每个点记录最长匹配长度即可
对多个串,考虑对最短串建 SAM,枚举每个串做两个串的匹配
我们在 SAM 上每个节点记录当前轮最长匹配长度 \(w_i\),历史匹配最小值 \(\text{mn}_i\)
每轮匹配结束后,令 \(\text{mn}_i \leftarrow \min\{\text{mn}_i, w_i\}\);最终 \(\max\{\text{mn}_i\}\) 即为答案
注意,若 SAM 上的节点 \(i\) 匹配,\(\text{parent tree}\) 上 \(i\) 的每个祖先也都能匹配:
-
在两个串匹配时,选祖先匹配肯定不如选自己匹配优,因此可以不管
-
在多个串匹配时,由于需要对历史匹配最小值取 \(\min\),选自己不一定优
因此,我们记录每个点当前轮的最长匹配长度 \(w_i\),在匹配结束后遍历 \(\text{parent tree}\) 更新即可
具体的,设 \(v \in \text{son}(u)\),若 \(w_v > 0\),则令 \(w_u \leftarrow \max(w_u, \text{len}(u)+1)\)
事实上,每次需要遍历 \(\text{parent tree}\) 更新正是我们选择最短串建 SAM 的原因
时间复杂度 \(O(\sum |s_i|)\)
CF1054F - Electric Scheme (二分图求最大独立集方案 - 布尔变量赋值法)
首先显然有解,在每个火花 \((x_i, y_i)\) 处画两次 \((x_i, y_i, x_i, y_i)\) 即可
注意到可以将连一根长线拆成很多相邻的短线,于是考虑将火花按分别按 \(x, y\) 坐标排序,相邻火花间连线
令初始时答案为 \(2n\),我们可以选择合并一些线,每次合并答案会 \(-1\),求最多能合并多少次
显然,有冲突关系的线 (相交且不交于端点) 我们只能选 \(1\) 根;考虑直接在线间连边刻画冲突关系
问题转化为对于点数为 \(O(n)\),边数为 \(O(n^2)\) 的二分图求最大独立集方案
考虑布尔变量赋值法:
-
给每个点赋布尔变量 \(x_i\),以最小割为例,令 \(x_S = 1, x_T = 0\)
若 \(x_i = 1\) 则 \(i\) 属于 \(S\) 连通块,\(x_i = 0\) 则属于 \(T\) 连通块
则最小割可转化为最小化 \(\displaystyle \sum_{(u_i \rightarrow v_i, w_i) \in E} w_ix_{u_i}(1-x_{v_i})\)
-
类似的,令 \(x_i = 1\) 表示选择点 \(i\),\(x_i = 0\) 表示不选
则最大独立集可转化为最小化 \(\displaystyle \sum_{u \in V} -x_u + \sum_{(u_i, v_i) \in E} \infty x_{u_i}x_{v_i}\)
考虑将最大独立集的式子转化为最小割的式子
注意到 \(\infty x_{u_i}x_{v_i}\) 不好处理,我们希望将 \(x_{v_i}\) 变为 \((1-x_{v_i})\);由于原图为二分图,可以将所有右部点取反
具体的,对右部点,更改定义为 \(x_i = 1\) 表示不选点 \(i\),\(x_i = 0\) 表示选;原式转化为:
\(\displaystyle \sum_{u \in L} -x_u + \sum_{v \in R} -(1-x_v) + \sum_{(u_i, v_i) \in E} \infty x_{u_i}(1-x_{v_i})\)
\(\displaystyle = \sum_{u \in L} -x_u + \sum_{v \in R} x_v + \sum_{(u_i, v_i) \in E} \infty x_{u_i}(1-x_{v_i}) - |R|\)
考虑将 \(-x_u\) 与 \(x_v\) 也转化为 \(x_{u_i}(1-x_{v_i})\) 的形式,注意到 \(x_S = 1, x_T = 0\),转化为:
\(\displaystyle = \sum_{u \in L} x_S(1-x_u) + \sum_{v \in R} x_v(1-x_T) + \sum_{(u_i, v_i) \in E} \infty x_{u_i}(1-x_{v_i}) - |L| - |R|\)
常量扔掉,直接建模最小割即可:
-
对 \(u \in L\),连边 \((S \rightarrow u, 1)\)
-
对 \(v \in R\),连边 \((v \rightarrow T, 1)\)
-
对 \((u_i, v_i) \in E\),连边 \((u_i \rightarrow v_i, \infty)\)
事实上,易得这部分每条边流量最大为 \(1\),可以直接将容量改为 \(1\),这样 dinic 是 \(O(m \sqrt n)\) 的
-
那么直接 dinic 即可
跑完后 dfs 搜出 \(S\) 的联通块,最大独立集方案即为:
- 满足 \(u_i \in L\) 且 \(x_{u_i} = 1\) (在 \(S\) 的连通块) 的 \(u_i\)
- 满足 \(v_i \in R\) 且 \(x_{v_i} = 0\) (在 \(T\) 的连通块) 的 \(v_i\)
时间复杂度 \(O(n^2 \sqrt n)\)
8.28
CF1416F - Showing Off (拆环 + 二分图 - 关键点优先匹配)
对位置 \((x, y)\),若存在相邻位置 \((x', y')\) 满足 \(b_{x', y'} < b_{x, y}\),则可让 \((x, y)\) 走到 \((x', y')\),再用 \(a_{x, y}\) 补齐差值
显然,若对所有相邻位置都有 \(b_{x', y'} > b_{x, y}\),则无解
那么难点只在于处理 \(b_{x', y'} = b_{x, y}\) 的情况;这实际上要求 \((x, y)\) 与 \((x', y')\) 在一个环内
注意到环长必为偶数,考虑拆成许多二元环处理,而二元环就相当于两两匹配
问题转化为将满足 \(b_{x', y'} = b_{x, y}\) 的 \((x', y')\) 与 \((x, y)\) 连边,求最大匹配方案
但事实上,有些 \((x, y)\) 已经可以连到相邻且小于它的位置,这种位置匹配不上也没关系
我们称必须匹配的点为 "关键点",则我们需要让关键点优先匹配
有的先匹配有的后匹配做不了,考虑通过 "让非关键点匹配机会变多" 降低其 "匹配优先级",具体的:
-
创建一些 "幻想点" 将左右部点数量补齐
-
在所有不是关键点的点 (即 "非关键点" 与 "幻想点") 间两两连边
这一步可以创建一个虚点优化建图
跑最大匹配,若不是完美匹配则无解,若是则搜出方案即可
时间复杂度 \(O(nm \sqrt{nm})\)
QOJ3555 - Chameleon's Love (观察性质 + 二分图独立集)
部分分 1:所有恋慕关系都是双向的
若选择相互恋慕的一对变色龙,它们会互相交换颜色,不改变颜色总数 (即颜色总数 \(=\) 询问集合大小)
那么颜色总数改变 \(\iff\) 选择了至少一对颜色相同的变色龙
考虑对每个变色龙 \(i\) 二分一次,\(i\) 与 \(S\) 中的点有连边 \(\iff\) \(\text{Query}(S \cup i) > \text{Query}(S)\)
询问次数 \(O(n \log n)\)
这个部分分启示我们观察什么时候颜色总数会改变
部分分 2:\(O(n^2)\) 次询问
考虑对每两只变色龙都问一次,若颜色总数为 \(1\) 则连边
对变色龙 \(i\),设 \(a_i\) 为 \(i\) 恋慕的变色龙,\(b_i\) 为恋慕 \(i\) 的变色龙,\(c_i\) 为与 \(i\) 同色的变色龙
显然,\(i\) 只可能和 \(a_i, b_i, c_i\) 连边
若 \(i\) 的度数为 \(1\),则必有 \(a_i = b_i\),此时 \(i\) 连向的点就是 \(c_i\)
若 \(i\) 的度数为 \(3\),考虑分讨询问 \(\{i, a_i, b_i, c_i\}\) 子集的情况:
- \(\{i, a_i, b_i, c_i\}\),则 \(i \rightarrow a_i\) 且 \(b_i \rightarrow i\) 且 \(i = c_i\),颜色总数为 \(2\)
- \(\{i, a_i, b_i\}\),则 \(i \rightarrow a_i\) 且 \(b_i \rightarrow i\),颜色总数为 \(2\)
- \(\{i, b_i, c_i\}\),则 \(b_i \rightarrow i\) 且 \(i = c_i\),颜色总数为 \(1\)
- \(\{i, a_i, c_i\}\),则 \(i \rightarrow a_i\),颜色总数为 \(2\)
- \(\{i, a_i\}\) 与 \(\{i, b_i\}\) 与 \(\{i, c_i\}\) 颜色总数均为 \(1\)
容易发现情况 \(\{i, b_i, c_i\}\) 是特殊的,找出这种情况后将所有 \(i \rightarrow a_i\) 拿出来,用排除法找出 \(c_i\) 即可
询问次数 \(O(n^2)\)
这个部分分启示我们重点关注 \(a_i, b_i, c_i\)
部分分 3:性别已知
在部分分 2 的基础上,我们只需优化询问次数即可
对变色龙 \(i\),考虑找与其性别不同的变色龙组成的集合 \(S\) 询问
有了部分分 2 的启发,容易发现只有 \(a_i, b_i, c_i\) 至少一个在 \(S\) 中时,颜色总数改变
那么可以通过 \(3\) 次二分找出 \(a_i, b_i, c_i\),剩下的与部分分 2 相同
询问次数 \(O(n \log n)\)
正解:
考虑优化部分分 3 的做法
注意到我们实际上不需要 \(S\) 中性别均相同,只需要 \(S\) 内部无连边即可
这启示我们维护二分图独立集
考虑增量构造,维护两个独立集 \(S_0, S_1\),每次加入新节点 \(i\)
容易通过部分分 3 的方式对 \(S_0\) 与 \(S_1\) 分别询问,二分出 \(i\) 与 \(S_0, S_1\) 中的点的所有连边
由于 \(n \le 500\),直接连无向边,重新黑白染色构造出新的 \(S_0, S_1\) 即可
最终找到所有连边后,可用部分分 2 的做法求出答案
询问次数 \(O(n \log n)\),时间复杂度 \(O(n^2 \log n)\)

浙公网安备 33010602011771号