2023 省选做题记录 2.0
- 1.27
- 1.28
- 1.29
- 1.30
- 1.31
- 2.1
- 2.2
- [CF1778D] Flexible String Revisit(数学)
- [CF1778E] The Tree Has Fallen!(线性基 + 换根 + 前后缀信息合并)
- [CF1778F] Maximizing Root(DP + 枚举)
- [CF1270E] Divide Points(奇偶构造)
- [ARC095F] Permutation Tree(构造 + 性质)
- [POI2009] LYZ-Ice Skates(Hall 定理 + 线段树)
- [CF1775F] Laboratory on Pluto(贪心 + 二分 + DP)
- [ARC096E] Everything on It(容斥 + 斯特林数)
- 2.3
- 2.4
年后就要开新坑(确信)。
1.27
[CF786E] ALT(最小割 + 树剖 & 线段树优化建图)
有一个显然的最小割建图方法:居民为左部点,守卫为右部点,\(S\) 向居民连流量为 \(1\) 的边,守卫向 \(T\) 连流量为 \(1\) 的边,每个居民向对应散步路径上的每一个守卫连流量为 \(\infty\) 的边,最小割就是答案。
然而数据范围太大,显然直接 Dinic 会因为边数太多而过不去。因此我们需要考虑优化建图。
散步路径是在树上,所以可以使用树剖 + 线段树优化建图,这样边数就是 \(\mathcal{O}(n\log^2 n)\) 级别的了。直接跑 Dinic 可以接受。
输出方案可以直接查看割集。具体来说割集就是残量网络上一端与 \(S\) 连通、一端与 \(T\) 连通的边。
代码:https://paste.ubuntu.com/p/RkRPFyVpjz/。
[六省联考 2017] 寿司餐厅(最大权闭合子图)
因为可以取多次,所以可以忽略掉每次只取一个区间的限制。
将每个区间 \([l,r]\) 都建一个点。因为取了 \([l,r]\) 之后其所有子区间的贡献都会被算入,所以可以考虑最大权闭合子图模型。将 \([l,r]\) 向 \([l+1,r]\) 和 \([l,r-1]\) 代表的点连一条流量为 \(\infty\) 的边(\([l,r]=[l+1,r]\cup[l,r-1]\),且这样做之后整张图按照区间长度分层)。如果 \(d_{l,r}\) 是正数就由 \(S\) 向其连流量为 \(d_{l,r}\) 的边,否则就由它向 \(T\) 连流量为 \(-d_{l,r}\) 的边。
接下来考虑花费钱数的限制。对于 \(cx\) 可以直接 \(d_{i,i}\leftarrow d_{i,i}-a_i\);对于 \(mx^2\) 可以对于每一个 \(a\) 都新建一个点,由 \([i,i]\) 对应的点向 \(a_i\) 对应的点连一条长度为 \(\infty\) 的边,再由 \(a_i\) 对应的点向 \(T\) 连一条长度为 \(ma_i^2\) 的边。
所有正权和减去最小割就是答案。
代码:https://paste.ubuntu.com/p/Fyvw8MHkMx/。
[TJOI2010] 电影迷(最大权闭合子图)
按照正常的最大权闭合子图一样建图,\(S\) 连正权,负权连 \(T\)。
不同的是有向边的权值不再是 \(\infty\) 而是 \(d\) 的代价。
代码:https://paste.ubuntu.com/p/X7WnDZySd4/。
[CEOI2008] order(最大权闭合子图)
建三层点,分别代表工作、工序和机器。
\(S\) 向工作连流量为收入的边;每个工作向对应的工序连流量为 \(\infty\) 的边;每道工序向所需的机器连流量为 \(b\) 的边;每个机器向 \(T\) 连流量为购买费用的边。
收入之和减去最小割就是答案。
代码:https://paste.ubuntu.com/p/9JKmT5RpQM/。
[WC2007] 剪刀石头布(费用流)
因为最终的图是完全图,所以可以考虑用所有的三元组个数 \(\binom{n}{3}\) 减去不合法的三元组个数。
不合法的三元组即不是一个三元环,它满足:一个点入度为 \(2\),一个点出度为 \(2\),一个点入度和出度都为 \(1\)。
可以只使用出度来作为贡献方式,那么最终状态下三元环个数就为 \(\binom{n}{3}-\sum\binom{out_i}{2}\)。
因为 \(\binom{out_i}{2}\) 是一个凸函数,所以考虑将贡献差分。
考虑每一条没有定向的边,对其新建一个点,\(S\) 向它连流量为 \(1\) 费用为 \(0\) 的边,它向两个端点分别连流量为 \(1\) 费用为 \(0\) 的边。每个点向 \(T\) 连若干条边,流量均为 \(1\) 且费用形如 \(\binom{k}{2}-\binom{k-1}{2}\)。\(k\) 的范围应该在原图中该点的出度和其最大可能出度(即 \(n-1-\) 原图中该点的入度)之间。
跑最小费用最大流。输出方案很好办,直接考虑每条未定向的边对应的点的流量流向了哪里。
代码:https://paste.ubuntu.com/p/R47n6584Fp/。
[CF1264E] Beautiful League(费用流)
和上一题一模一样。
代码:https://paste.ubuntu.com/p/65fM7cP43P/。
1.28
[JSOI2016] 飞机调度(最短路 + DAG 最小路径覆盖)
首先可以用 Floyd 求出 \(g_{i,j}\) 表示从 \(i\) 出发到达 \(j\) 且 \(j\) 可以再次起飞所需要的最少时间。
考虑建一张 DAG,每个航班代表一个点,第 \(i\) 个航班向第 \(j\) 个航班连边当且仅当在第 \(i\) 个航班飞行完的飞机还可以赶上第 \(j\) 个航班的出发时间,也就是可以有一架飞机共用这两个航班。具体来说条件就是 \(D_i+T_{X_i,Y_i}+P_{Y_i}+g_{Y_i,X_j}\le D_j\)。
这张 DAG 的最小路径覆盖数就是答案。
代码:https://paste.ubuntu.com/p/g3RfFsfwnR/。
[JOI Open 2020] 黑白点(贪心 + 构造)
一道神奇的题目。
考虑类似下图这种匹配:

容易发现交换两个黑点的匹配点之后答案不会变劣。那么我们断言最优解中一定不存在上述匹配。
进一步观察性质。可以发现,如果我们把黑点序列和白点序列分别拿出来,那么相邻的黑点一定会匹配相邻的白点。也就是说一定是两个序列循环移位匹配。
接下来考虑怎么求相交数。设一条线段的长度为它划分出的两个圆弧中点数较少的那一个圆弧中的点数(不包括端点)。显然答案的上界就是所有线段的长度之和。
但这样还是不好进行最优化的操作。于是可以考虑反过来求不与它相交的线段个数。即设一条线段的长度为 \(l\),那么我们实际上就把所求变成最小化 \(\sum(n-l)\)。
把所有黑点翻到它的正对面去,这样原来长度为 \(l\) 的线段现在的长度变成了 \(n-l\)。所以我们的问题就是:将黑点白点两两匹配,最小化匹配的长度之和。
如果问题是在序列上,考虑每一个单位长度的贡献,实际上就是其左侧黑点和白点的个数差。这个下界显然可以达到。
换一种描述方式,考虑将序列对应到一条折线上,初始坐标为 \((0,0)\),每次横坐标 \(+1\),遇到黑点纵坐标 \(-1\),白点纵坐标 \(+1\),答案就是所有点纵坐标绝对值之和。
将其转移到环上考虑,也就是将折线切开之后交换再拼接,可以看成折线上下平移。那么我们就是要最小化 \(\sum|y_i-D|\),其中 \(D\) 是平移的长度。显然取中位数即可。
1.29
[CTSC2008] 祭祀(Dilworth 定理 + DAG 最长反链输出方案)
由 Dilworth 定理,最长反链 = 最小链覆盖。所以我们可以先做一遍传递闭包,然后转化为最小路径覆盖。这个可以拆点二分图最大匹配直接做。
接下来考虑后两问。先求出第三问的答案,即哪些点可能出现在最长反链中。可以把这个点以及它能到的点、能到它的点全部删掉,再求一遍最长反链,看它 \(+1\) 之后是否为原答案,如果是就说明它可能出现在最长反链中。
再考虑第二问,我们考虑对所有点染色。每次取出一个可能在最长反链中且还没有被染色的点,将它染上一个新颜色,并且将它能到的点和能到它的点都染上这个颜色。最终在每个颜色中取一个点出来就是一种可能的方案。
时间复杂度 \(\mathcal{O}(n^3\sqrt n)\)。
代码:https://paste.ubuntu.com/p/cgpp45mvq5/。
其实只要做第二问的话有一种复杂度更优的做法。具体做法见下(直接 copy 了):
我们从左边所有未匹配的点开始 dfs,每次从左往右只走未匹配边,从右往左则只走匹配边,并标记所有的点。
二分图的一种最小点覆盖就是左侧的未访问点加上右侧的已访问点;
二分图的一种最大独立集就是这些点的补集(对于二分图所有点全集);
而原图的一种最大独立集就是由所有其编号对应的二分图左侧点和右侧点都在二分图最大独立集中的点组成(或者说由所有其编号对应的二分图左侧点已访问且右侧点未访问的点组成)。
(引自 [CTSC2008]祭祀 Piwry 的题解)
代码可以看下一个题。
[CF590E] Birthday(AC 自动机 + Dilworth 定理 + DAG 最长反链输出方案)
如果 \(i\) 是 \(j\) 的子串,那么连边 \(i\to j\)。问题就变成在这一张 DAG 上找最长反链。套用上一题的做法就行。
注意到 \(\sum|s_i|\) 很大,那么我们不能在 fail 树上递归。对于每个点,找到 fail 树上离它最近的是某个串结尾结点的祖先 \(pos_i\),这个可以在求 fail 数组的时候顺便求出。建图的时候遍历从根到这个串结尾结点的所有点,如果某个点的 \(pos_i\ne 0\),那么就由这个串向 \(pos_i\) 连边。如果 \(fail_{ed}\) 的祖先也有某个结点是另一个串的结尾,那么这个串也可以向它连边。
数组开小调了半个多小时是怎么一回事呢。
代码:https://paste.ubuntu.com/p/3wbBzPVNPN/。
[APIO/CTSC2007] 数据备份(反悔贪心)
差分一下,将题意转化为:在 \(n-1\) 个数中选择 \(k\) 个不相邻的数使得和最小。
一个显然错误的想法是将所有数放入小根堆中,每次取出最小的一个未被标记的数,将它左右标记为不能取,这样做 \(k\) 轮。考虑怎么进行反悔,取出一个未被标记的数之后并不将其马上删除,而是将它的权值设为左右两边的数的权值之和减去它的权值,再将它左右的数标记为不能取。使用双向链表维护这一过程。
代码:https://paste.ubuntu.com/p/yXWtrCmJVF/。
[CF436E] Cardboard Box(反悔贪心)
反悔贪心一般考虑直接选择和反悔有哪些操作,代价分别是什么,然后再开若干个堆去维护这些代价。
在这道题中,考虑每次增加一颗星星。直接选择有两种方法:
- 选一个没有星星的加一个,代价为 \(a_i\);
- 选一个有一个星星的加一个,代价为 \(b_i-a_i\)。
反悔也有两种方法:
- 把一个有一个星星的删一个,再把另一个没有星星的加两个,代价为 \(-a_i+b_j\);
- 把一个有两个星星的删一个,再把另一个没有星星的加两个,代价为 \(a_i-b_i+b_j\)。
那么可以开五个小根堆,分别维护 \(a_i\),\(b_i-a_i\),\(b_j\),\(-a_i\),\(a_i-b_i\)。
代码:https://paste.ubuntu.com/p/RrsxH3sBsZ/。
1.30
[CF1019C] Sergey's problem(归纳构造)
玄妙归纳构造。
任取一点 \(u\),删去 \(u\) 和所有 \(u\) 指向的点。然后在剩下的图上构造解。再考虑是否有集合内的点指向点 \(u\),如果没有就将 \(u\) 加入集合。
正确性显然。
启发:图上构造考虑拿出一个点之后递归构造!
代码:https://paste.ubuntu.com/p/yZSDkj8RYh/。
[NOIP2020] 移球游戏 80 分做法(归纳构造)
构造题考虑一种基本操作。在本题中基本操作就是 将某个球提到它所在的柱子的最上面。
具体做法如下:
- 在另一个满的柱子 \(i\) 上取一个球放入空柱子;
- 将要提的球上方的所有球放入空柱子;
- 将要提的球放到柱子 \(i\) 上;
- 将空柱子清到只剩一个球,清的方法就是放入要提的球所在的柱子;
- 将这个剩下的球放入柱子 \(i\)。
接下来考虑递归构造,每次构造一根同色的柱子,然后就变成大小 \(-1\) 的子问题。
设要构造的颜色为 \(col\)。将所有柱子中颜色为 \(col\) 的球提到其所在柱子的上方,然后让空柱子变成同色柱,再选择一个柱子用它去填补其他柱子的空缺,使得它变成空柱子。
可以有很多常数优化:
- 对于同一根柱子,可以一次性把所有同色球一起提。具体来说就是在另一个柱子 \(i\) 里拿 要提的柱子中的该颜色球的数量 个球放到空柱子里,将要提的柱子清空,颜色为 \(col\) 的放入 \(i\),否则放入空柱子。还原同理。
- 选择操作次数最少的颜色作为 \(col\)。
- 每次做完不需要把空柱子清空,可以把另一个已经清空的柱子当作空柱子。也就相当于两根柱子的功能交换。
这样大概就有 80 分了。
正解好像要分治。摆了。
80 分代码:https://paste.ubuntu.com/p/GFXcvqFhpV/。
[CF1787G] Colorful Tree Again(set)
题目可以转化为给定树上若干条边集不交的简单路径,每条路径有权值,每次改变每个点是否被封锁,一条路径的权值有效当且仅当路径上所有点都没有被封锁,问所有权值有效的路径中最大的路径权值。
首先求出所有路径的权值。
注意到路径边集不交这个性质很强,这意味着每个点到它父亲的边最多只可能属于一条路径,可以把这个路径的编号记录下来。
考虑对每个点维护一个 set,存储以它为最浅点的当前权值有效的路径的权值。同时维护一个答案 set \(ans\),就是将每个点 set 里最大的权值放入。
封锁一个点的时候,先在 \(ans\) 里删掉它维护的 set 的最大值,然后考虑它和父亲那条边属于的路径。如果它是这条路径中现在唯一一个被删的点,就在该路径最浅顶点处的 set 里删掉该路径的权值,并实时更新 \(ans\)。
对一个点解除封锁同理。实际上就是反过来。
代码:https://paste.ubuntu.com/p/rw6JtBvTv8/。
1.31
[洛谷 P8851]『JROI-7』T2nz.(博弈论)
作为后手时,考虑将每行 \(2n\) 个格子两两配对,先手选一个格子的时候后手选和该格子配对的那一个。这样就可以证明答案上界为 \(2^n\)。
作为先手时,到之前的行中有可能成为该行的行。对于这些行,计算出每一列存在的白格的个数,选择白色最多的一列下。这样每次至少可以排除一半的可能重复的行。这样构造保证了前 \(2^n\) 行互不相同,后面的若干行随便放就行。
代码:https://paste.ubuntu.com/p/sfcjmNMgQn/。
[ARC152D] Halftree(构造)
什么人类智慧题???
因为有偶数条边,所以当 \(n\) 为偶数时无解。接下来考虑通过构造说明 \(n\) 为奇数时一定有解。
考虑连边 \(i\to(i+k)\bmod n\)。那么整张图就会有 \(\gcd(n,k)\) 个大小为 \(\frac{n}{\gcd(n,k)}\) 的环。
连边有两种:连圆环和环间“平行边”。先考虑连圆环,然后交替留出 \(1/3\) 条边的缺口,再连平行边。
如果最终要是一条链就只需要考虑动态地调整缺口的位置。
代码:https://paste.ubuntu.com/p/BsWrk7323Q/。
[CF1383D] Rearrange(构造 + 队列)
考虑从大到小填数。初始矩阵为空。
如果这个数是某行或者某列的最大值,那么就新开一行 / 一列并在当前位置填入该数,然后将其左边或者上方的位置按照从右往左或从下往上的顺序加入队列。
最大值的集合正确性显然。而对于单峰的限制,左边的元素在队列中塞入的先后顺序是合法的,而又因为元素是降序填入的所以右边的一定比左边的要小,上下同理,所以一定合法。
代码:https://paste.ubuntu.com/p/zpX2SFg5B4/。
[POI2011] 棒棒糖 Lollipop(构造 + 思维 + 前缀和)
关键性质:如果 \(k\) 可以被表示,那么 \(k-2\) 也可以。
证明:考虑形成 \(k\) 的子段,如果它两边都是 \(1\),那么可以删掉它的两侧;否则一定有一边是 \(2\),删掉这个 \(2\) 就行。
那么首先求出整个序列的和 \(sum\),那么 \(\le sum\) 的和 \(sum\) 奇偶性相同的数都能被表示。剩下的任务就是求出奇偶性和 \(sum\) 不同的最大子段和。显然是通过删去左边或右边若干个 \(2\) 得到,具体就是删掉 \(2\) 较少的那一边的所有 \(2\) 和后一个 \(1\)。
构造可以通过那个性质的证明显然地得出。
2.1
[洛谷 P3599] Koishi Loves Construction(构造 + 打表)
打表可以发现:
- 对于 Task1,当 \(n=1\) 或 \(n\) 为偶数时有解,可以构造形如
n 1 n-2 3 n-4 ... 4 n-3 2 n-1的排列。 - 对于 Task2,当 \(n=1/4\) 或 \(n\) 为质数时有解,可以构造前缀积为
1 2 3 4 ... n-1 0的排列。
证明对于 Task1 来说比较简单,大概就是该排列可以看成 \(\bmod n\) 意义下的 0 1 -2 3 -4 ...,前缀和就是 0 1 -1 2 -2 ...,显然不会有相等的数。
对于 Task2,可见题解,懒得写了。大概就是说要证明逆元唯一。
代码:https://paste.ubuntu.com/p/yszHTpgt2B/。
[AGC029C] Lexicographic constraints(构造 + 二分 + map)
显然答案具有可二分性。
如果 \(A_i> A_{i-1}\),那么可以在 \(A_{i-1}\) 后面的位放入字典序最小的字符;否则 \(A_i\le A_{i-1}\),需要舍弃掉后 \(A_{i-1}-A_i\) 位,并且在 \(A_{i-1}\) 这一位上进一位(即字符字典序 \(+1\)),如果这一位字典序已经达到最大就需要往前进位。最后如果第 \(0\) 位要进位就说明答案不行。
整个过程可以用 map 模拟。
代码:https://paste.ubuntu.com/p/vKNYXR2G3T/。
[ARC098F] Donation(贪心 + Kruskal 重构树)
注意到到达某一个节点至少需要 \(A_u\) 元。所以显然有如下关键性质:
- 对于每个节点,我们肯定是在最后一次到达它的时候进行捐赠。
证明考虑如果不是最后一次捐赠,那么调整之后两次到达之间剩余的钱数会增加。因此如果在最后一次到达才捐赠严格不劣。
接下来这一步也很神奇:设 \(c_i=\max(a_i-b_i,0)\) 表示在捐赠完这个点之后剩余的最少钱数。那么逆向考虑,每次走到一个点的时候至少要 \(c_i\) 元,并且第一次走到还可以得到 \(b_i\) 元。问最终的最少钱数。
显然如果按照 \(c_i\) 单调不降的顺序访问每个点是不劣的。这启发我们点权转边权,建立 Kruskal 重构树。具体来说就是把每条边的边权设为 \(w_i=\max(c_u,c_v)\),将边从小到大排序加入 Kruskal 重构树,这样保证两点之间经过的 \(\max c\) 最小。
接下来考虑 CF1578L Labyrinth 类似的套路:在 Kruskal 重构树上 DP,考虑按照某种顺序(先左后右 / 先右后左)遍历每个节点的两个子树,将这两种顺序的贡献取 \(\min\)。
这一题中就是对每个节点维护 \(ans_i\) 和 \(sum_i\) 表示走完这个子树所需的最少初始钱数和子数内的 \(\sum b_i\)。转移就是 \(ans_i=\min(\max(w_i,ans_{ls})+sum_{rs},\max(w_i,ans_{rs})+sum_{ls})\)。
代码:https://paste.ubuntu.com/p/sRRKcnd4TG/。
[ARC098E] Range Minimum Queries(枚举 + 二分)
考虑枚举删去的最小值 \(mn\),二分最大值 \(mx\)。显然选择的区间不能包含 \(<mn\) 的数,那么整个序列就以 \(<mn\) 的数作为分割符被分成了若干段。
对每一段单独计算其最多的操作次数。设有 \(cnt\) 个值域在 \([mn,mx]\) 的数,这一段的长度为 \(len\),那么操作次数最多为 \(\max(0,\min(cnt,len-k+1))\)。看起来比较显然?
代码:https://paste.ubuntu.com/p/tbMPPW48yx/。
[NOI2018] 屠龙勇士(扩展中国剩余定理 + multiset)
使用哪一把剑可以用 multiset 求前驱得到。假设第 \(i\) 条龙用的是攻击力为 \(b_i\) 的剑。
然后就是 exCRT 的老套路了:合并同余方程。
假设之前得到的解是 \(ans\),之前模数的 lcm 为 \(M\)。那么我们就是要找到一个 \(x\) 使得 \(b_i(ans+Mx)\equiv a_i\pmod{p_i}\)。
可以转化为线性同余方程 \(b_iMx+p_ik = a_i-b_ians\)。接下来设 \(A=b_iM\),\(B=p_i\),\(C=a_i-b_ians\),\(d=\gcd(A,B)\)。
使用 exgcd 求出 \(Ax+Bk=d\) 的解。如果 \(C\bmod d\ne 0\) 就说明无解。
接下来就可以知道原方程的解就是 \(x\times \frac{C}{d}\)。代入即可。
合并的时候注意同余方程的模数已经变了。是 \(\frac{B}{d}\)。
还需要注意答案要大于 \(\max(\lceil\frac{a_i}{b_i}\rceil)\)。
[CF976F] Minimal k-covering(补集转化 + 二分图 + 最大流)
思路挺神奇的一题。
网络流的时候我们是限制每条边最多流多少的流量,而这里我们要求最小化边集大小。所以可以考虑补集转化,最大化不在边集里的边数。
如果要求每个点最少被覆盖 \(k\) 次,那么就是说最多有 \(deg_i-k\) 条和点 \(i\) 相连的边没有被选。那么这样可以建一张显然的二分图模型。中间有流量的边就是不在边集里的边。
但是如果每次有一个新的 \(k\) 就重新建图显然是不现实的。考虑从大到小枚举 \(k\),每次每个点的流量实际上就增加了 \(1\),在残量网络上直接加边后跑最大流就行。
代码:https://paste.ubuntu.com/p/ncKyfQVfrQ/。
2.2
[CF1778D] Flexible String Revisit(数学)
题解有两种做法。一种是巨大复杂直接推式子。很神奇啊。贴这里了。好像这就是赌徒输光问题?

还有一种就是已知 \(f(0)=0\),\(f(1)=2^n-1\),那么可以推出 \(f(i)=\frac{nf(i-1)-(i-1)f(i-2)-n}{n-i+1}\)。这样就可以递推了!注意特判 \(f(n)=f(n-1)+1\)。
std1:https://paste.ubuntu.com/p/bVk4FYMrYG/。
std2:https://paste.ubuntu.com/p/bVz6WGKMCB/。
题解评论区也有一种神奇的做法:
- 设 \(f_i\) 为第一次从有 \(i\) 个差异到有 \(i-1\) 个差异的期望时间。答案是 \(\sum\limits_{i=1}^k f_i\)。
- 转移很神奇:\(f_i=1+\frac{n-i}{n}(f_{i+1}+f_i)\)。
- 有 \(\frac{i}{n}\) 的概率直接走到;
- 有 \(\frac{n-i}{n}\) 的概率走到 \(i+1\),这个时候再走回来需要 \(f_{i+1}+f_i\)。
- std3:https://paste.ubuntu.com/p/DfYFGxPfcC/。
[CF1778E] The Tree Has Fallen!(线性基 + 换根 + 前后缀信息合并)
题意即求 原树中某个子树的最大异或和 和 原树中去掉某个子树的最大异或和。
前者可以直接 dfs 的时候线性基合并解决。
后者考虑类似换根 dp。设 \(g_u\) 表示去掉 \(u\) 和 \(u\) 子树的线性基。转移到某个儿子信息的时候考虑预先求出每个前后缀的线性基,然后直接把 \(g_u\) 和某个儿子前的前缀和其后的后缀对应的线性基合并。
其实转成 dfs 序后可以直接在 dfs 序上做前后缀线性基合并。
代码:https://paste.ubuntu.com/p/vq6fwyhHBF/。
启发:对于要求去掉某个子树的信息,可以考虑自顶向下 dfs,并使用前后缀信息合并来处理兄弟结点的信息。
[CF1778F] Maximizing Root(DP + 枚举)
因为每个点最多被操作一次,所以答案最多为 \(a_1^2\)。
值域很小,考虑枚举在根处操作的时候所有数的 \(\gcd\)。显然这个数越大越好。那么根节点的每个子树的 \(\gcd\) 都要是这个数的倍数。
设 \(dp_{u,i}\) 表示要让 \(u\) 子树内的所有数的 \(\gcd\) 是 \(i\) 的倍数所需的最少操作次数。如果 \(a_u^2 \bmod i\ne 0\) 那么 \(dp_{u,i}=\infty\)。
枚举在 \(u\) 操作时 \(u\) 子树的 \(\gcd\),设为 \(o\),那么就要满足 \((a_u\times o)\bmod i=0\) 且 \(i\bmod o=0\)。并且它的儿子的子树要满足 \(\gcd\) 是 \(\operatorname{lcm}(o,\frac{i}{o})\) 的倍数。因为根据上面两个条件可知其儿子的子树 \(\gcd\) 一定要是 \(o\) 的倍数,而且在 \(\times o\) 之后要是 \(i\) 的倍数。
记得要记忆化一下。
代码:https://paste.ubuntu.com/p/qmP8TMg6sG/。
[CF1270E] Divide Points(奇偶构造)
感觉是一道挺神秘的题。虽然出发点很新颖。
将所有点按照横纵坐标的奇偶性分类。可以分为 \(S_{0,0},S_{0,1},S_{1,0},S_{1,1}\) 四个集合。接下来按照这四个集合是否非空讨论:
- 如果 \(S_{0,0},S_{1,1}\) 和 \(S_{0,1},S_{1,0}\) 中均有非空的集合,那么直接令 \(A=S_{0,0}\cup S_{1,1},B=S_{0,1}\cup S_{1,0}\);
- 如果 \(S_{0,0}\) 和 \(S_{1,1}\) 非空,那么 \(A=S_{0,0},B=S_{1,1}\);
- 如果 \(S_{0,1}\) 和 \(S_{1,0}\) 非空,那么 \(A=S_{0,1},B=S_{1,0}\);
- 否则这四个集合里只有一个非空,将每个点横纵坐标全部除以 \(2\) 之后再去更新集合。显然一定会出现某一时刻有两个或以上集合非空。
具体证明可以考虑集合内部和集合间点对距离的平方 \(\bmod 4\) 的余数。此处略去。
代码:https://paste.ubuntu.com/p/z6NY9TQYsx/。
[ARC095F] Permutation Tree(构造 + 性质)
考虑如何描述连边的过程:从小到大枚举每个数,将当前位置 \(i\) 和之前扫到的最大位置 \(mx\) 连边,并更新 \(mx\leftarrow\max(mx,i)\)。
我们称所有可以作为 \(mx\) 的位置为“局部最大值”。那么连边肯定只有 非局部最大值和局部最大值 以及 相邻两个局部最大值 这两种。所以最终树的形态肯定是毛毛虫。即每个点到直径的距离都 \(\le 1\)。而直径上的点就是所有局部最大值。
接下来考虑构造。假设现在已经确定了前 \(x\) 个数的位置,要在直径后面新接入一个有 \(y\) 个非直径上的点和它相连的点,那么就可以在第 \(x+y+1\) 个位置填上 \(x+1\),并在 \([x+1,x+y]\) 里从小到大填入 \(x+2\sim x+y+1\)。正确性显然。
从两个直径端点分别构造,输出字典序较小的那一个。
代码:https://paste.ubuntu.com/p/ZwpmDcgTBD/。
[POI2009] LYZ-Ice Skates(Hall 定理 + 线段树)
更清晰的题意可以见 Hydro-BZOJ。
注意到这是一个二分图匹配的模型,且只需要判断有无最大匹配,可以考虑 Hall 定理。
每次会在左侧增加 \(x\) 个 \(r\) 号点,并向右侧 \([r,r+d]\) 每个点连边。
注意到如果左侧选择连续型号的人,能够使得连向右侧的点集尽量小。那么也就是说 \(\forall1\le l\le r\le n-d,\sum\limits_{i=l}^r x_i\le k(r-l+1+d)\),移项后可以转化为 \(\sum\limits_{i=l}^r (x_i-1)\le kd\)。线段树求全局最大子段和即可判断。
代码:https://paste.ubuntu.com/p/WZw7SpjDrn/。
[CF1775F] Laboratory on Pluto(贪心 + 二分 + DP)
第一问很好做。直接二分 长 + 宽,面积最大显然是二者之差 \(\le 1\)。如果最大面积 \(\ge n\) 就说明该长度满足题意。
接下来考虑第二问。显然我们只能在四个角上挖空。空的形状一定是阶梯状,即不能有凹进去的部分;并且空的大小一定 \(\le\sqrt n\)。所以假设某个角上有 \(x\) 个空,那么方案数就是 \(x\) 的分拆数。求解分拆数很简单,设 \(g_{i,j}\) 表示 \(i\) 被拆分成 \(j\) 个数,转移有 \(g_{i,j}=g_{i-1,j-1}+g_{i-j,j}\),即新开一个数或者给之前的数全部 \(+1\),那么分拆数就是 \(\sum g_{x,i}\)。四个角显然独立,暴力卷积即可。
代码:https://paste.ubuntu.com/p/SMdYTshzGk/。
[ARC096E] Everything on It(容斥 + 斯特林数)
考虑容斥,钦定至少 \(i\) 个不合法。假设它们被分到了 \(j\) 个集合里,那么方案数就为 \(i+1\brace j+1\)。这是因为可以新加入一个元素 \(0\),和 \(0\) 在同一个集合里的元素就代表不出现。剩下 \(n-i\) 个元素在这 \(j\) 个集合里任意出现,方案数为 \(2^{(n-i)j}\);其它不包含不合法元素的集合也可以任选,方案数为 \(2^{2^{n-i}}\)。
所以答案为:
代码:https://paste.ubuntu.com/p/cybHjW7bgX/。
2.3
[RC-07] 心跳(打表 + 数学)
固定 \(B\),把所有 \(B\)-好的 的数打出来,做个差分,容易发现差分数组形如:
- \(n-1\) 个 \(1\);
- \(n-1\) 个 \(n-1\)、\(1\);
- \(n-1\) 个 \(n(n-1)\)、\(n-1\)、\(1\);
- \(n-1\) 个 \(n^2(n-1)\)、\(n(n-1)\)、\(n-1\)、\(1\)。
- ……
直接算就行。
证明可见官方题解:https://www.luogu.com.cn/blog/feecle6418/rc-07-ti-xie。
代码:https://paste.ubuntu.com/p/jKgCrdbQMt/。
[RC-07] Game Theory(博弈 + 结论)
挺神奇的一道题。判断 \(n-a_i\) 的异或和是否为 \(0\) 就行。
证明见官方题解。我也不是很懂啊。
代码就不放了。
[CF981F] Round Marriage(Hall 定理 + 断环成链 + 双指针)
一眼二分 + Hall 定理。每个点相连的边是一个区间,然而因为是一个环所以也有可能是一段前缀 + 一段后缀。所以考虑断环成链,具体的把 \(a_i\) 复制两遍再平移一个环长,\(b_i\) 复制四遍,那么每个点连的边就都是一段区间了。
如果只考虑 \([l,r]\) 的新郎,那么就需要判断 \([l-d,r+d]\) 内的新娘数量是否 \(\ge\) 新郎数量。可以双指针求出每个点能连到的最左边的点 \(tl_i\) 和最右边的点 \(tr_i\),问题就是要判断是否 \(\forall 1\le l\le r\le 2n,tr_r-tl_l+1\ge r-l+1\),移个项可以变成 \(tl_l-l\le tr_r-r\),维护 \(tl_l-l\) 的前缀 \(\max\) 和 \(tr_r-r\) 的后缀 \(\min\) 即可。
代码:https://paste.ubuntu.com/p/k5zjsRcz2g/。
2.4
立春了!
[ARC097E] Sorted and Sorted(DP)
转化一下题意,变成求解重排之后满足数字递增的条件下逆序对数最少。
考虑先预处理两个数组 \(b_{i,j}\) 和 \(w_{i,j}\),分别表示前面已经排了 \(i-1\) 个白球 / 黑球和 \(j\) 个黑球 / 白球,现在再放入一个黑球 / 白球能贡献的逆序对数。直接递推就行。
然后设 \(dp_{i,j}\) 表示已经排了 \(i\) 个球,有 \(j\) 个黑球的最少逆序对数。转移直接加上贡献就行。
代码:https://paste.ubuntu.com/p/5ZDWbxcr3G/。
[ARC099F] Eating Symbols Hard(哈希)
什么神秘哈希题。
发现这个过程本身就特别像哈希,于是可以往这一方面考虑。
每次整个序列位置上值的变化量是 \(\mathcal{O}(1)\) 的,所以可以直接算出每次操作完之后整个序列的哈希值 \(p_i\)。
那么一段子串操作序列 \([l,r]\) 的哈希值就是 \(p_r-p_{l-1}\) 再乘上起始位置的变化量。设 \(pos_i\) 表示进行完前 \(i\) 个操作后指针所在的位置。
为了方便我们可以将所有位置平移 \(n\) 位。假设进行完所有操作得到的哈希值为 \(all\)。
一个区间 $[l,r] $ 满足条件当且仅当 \(all=(p_r-p_{l-1})\times Base^{n-pos_{l-1}}\)。
移项可得 \(p_r=p_{l-1}+\frac{all\times Base^{pos_{l-1}}}{Base^n}\)。
从后往前扫描线就可以方便地统计。
代码:https://paste.ubuntu.com/p/j6jyKdHH8z/。
[AGC035C] Skolem XOR Tree(构造)
打表都差不多打出来了……但还是差一点……
打了个表发现 \(n=3\) 和 \(n=5\) 的时候总有一组解满足 \(2\sim n\) 和 \(1\) 都有连边。
真的都快做出来了,但就是差一点。唉。
这启发我们打表不能硬打,还要善于发现性质。
判断有无解:显然当 \(n=2^k\) 时无解,因为 \(n\) 和 \(2n\) 之间的路径怎么都凑不出来。
关键性质:\((2i)\oplus(2i+1)=1\)。
所以我们可以通过 \(\forall 1\le i<\lfloor\frac{n}{2}\rfloor\),连边 \((1,2i),(1,2i+1),(2i,2i+n+1),(2i+1,2i+n)\),来满足 \(2\sim n-[n\bmod 2=0]\) 的情况。
对于 \(1\) 号点,只需要将 \(n+1\) 和随便一个叶子连边就行了,因为 \(1\oplus (2i)\oplus(2i+1)\oplus1=1\)。
对于偶数的情况,单独考虑 \(n\)。因为 \(n\ne 2^k\) 所以 \(n\) 的二进制表示肯定至少有 \(2\) 个 \(1\)。那么连边 \((n,\mathrm{lowbit}(n)),(2n,n\oplus\mathrm{lowbit}(n)\oplus 1)\)。显然满足题意。
代码:https://paste.ubuntu.com/p/4fGRSkxYB9/。
[洛谷 P9033]「KDOI-04」XOR Sum(构造)
把最高位单独拿出来,只要看它能不能满足条件就行了。
代码:https://paste.ubuntu.com/p/fbp8gPhtG2/。
[洛谷 P9034]「KDOI-04」Again Counting Set(分类讨论 + 性质)
挺没有意思的,把条件可以进行一步步简化,最终得到可能满足条件的集合只有那么几种,分讨即可。
代码:https://paste.ubuntu.com/p/Frc244Qksx/。
[洛谷 P9035]「KDOI-04」Pont des souvenirs(组合计数)
条件就是说最大的两个数和 \(\le k+1\)。
枚举最大值,显然第二大的数最大为 \(\min(k+1-mx,mx)\)。讨论 \(mx\) 的范围进而推出 \(\min\) 是取前者还是后者。前面的数的方案数就是可重组合,把式子写出来之后上指标求和就是了。

浙公网安备 33010602011771号