代码源周作业44 题解
CF2167E khba Loves to Sleep!
二分答案,之后做法就很多了,其一是考虑每个点满足答案的是一段前缀 \([0, a_i - mid]\) 和一段后缀 \([a_i + mid, x]\),直接并的交就能得到答案了,但是细节比较多。所以考虑直接排序 \(a\),那么取所有 \([a_{i-1}+mid,a_i-mid]\) 的并即可。
CF2167F Tree, TREE!!!
考虑换一下统计贡献的方式,我们对每个点 \(x\) 统计有多少个点 \(u\),满足以 \(u\) 为根时 \(x\) 可以作为 LCA 被获取到,显然想让 \(x\) 为 LCA 最好的方式就是直接取 \(x\),然后再在 \(x\) 的子树中取 \(k-1\) 个点,换言之只要满足当 \(u\) 为根时,\(sz_x \ge k\) 即可。基于这个想法,我们先跑出以 \(1\) 为根时所有点的子树大小,再考虑换根时 \(x\) 的子树又变成了什么样呢。手玩可以发现,当取 \(x\) 外的点子树为根时,\(x\) 的子树大小不变;当取 \(x\) 子树内的点为根时,\(x\) 的子树大小变为 \(n\) 减去所在子树大小,那么我们直接统计即可。代码。
CF2144D Price Tags
\(V\) 范围不大,考虑直接枚举 \(x \in V\),那么 \(((k-1)x,kx]\) 区间内的数最后都会落在 \(k\),直接用喜欢的方式实现即可,复杂度调和级数。
CF1791G2 Teleporters (Hard Version)
挠餐了,看着这种题就立即触发 CNOI 被动开始找性质,首先注意到因为必须传送,所以每个点实际上是独立的,那么贡献就好算了,注意到起点固定为 \(0\),枚举第一个到的点之后直接二分即可,有一点小细节。代码。
CF1788D Moving Dots
想出来了!虽然只有*2000,首先考虑这个贡献整体算的话根本算不了,然后考虑枚举一对 \((x,y)\),算有多少方案这一对产生贡献,然后注意到 \(x\) 左侧有一段不能选,\(y\) 右侧有一段不能选,其余点选不选无所谓所以有 \(2^k\) 种,直接二分做也行,双指针也行,复杂度 \(O(n^2) \sim O(n^2\log n)\)。
ICPC2025沈阳 K
LMY 给的题,非常难。你考虑一次 \((x, y)\) 关于 \((x + dx, y + dy)\) 的对称,结果是 \((x, y) \rightarrow (x + 2dx, y + 2dy)\),然后当前操作的点 \((x, y) \rightarrow (x + dx, y + dy)\),这时候一个很神奇的观察来了,如果此时注意 \(\sum x\) 的变化,会发现 \(\sum x \rightarrow \sum x + 2dx\),\(y\) 的变化是同理的,然后我们再观察每次操作的点 \(x + dx\) 正好 \(\Delta\) 是 \(sumx\) 变化的一半,那考虑,目前我们知道了所有的终止坐标和所有的起始坐标,正好可以知道总的 \(\Delta x\) 和 \(\Delta y\),那么我们直接枚举起点,然后判断 \((x + \Delta x, y + \Delta y)\) 是否在终止集合里即可。(具体来说,因为当最后一步时,除了我们最后一次操作的点,别的点都到达了对应的终止位置,那么如果答案存在,则必然在终止集合里。)代码很好写。
[ARC112C] DFS Game
操作的过程是类似 dfs 的过程,这意味着当我们进入一棵大小为奇数的子树时,出来后先后手反转;偶数则不反转。另外,你发现真正能操作的只有选儿子并进入,所以我们考虑对这个 dp,我们设 \(dp_u\) 表示 \(u\) 子树内 \(u\) 先手能获得的最大价值,但棋子一旦落到 \(u\) 这棵子树上,先手需要先获得 \(u\) 上的金币,所以先获得选儿子权的是后手。那么我们考虑选择一棵子树 \(v\),分别对先后手的贡献都是多少。具体来说,当棋子移动到 \(v\),\(v\) 上的金币依然会被先手拿走,然后依旧后手接着行动,于是先手获得 \(dp_v\),后手获得 \(sz_v - dp_v\)。那么对于一个儿子 \(v\),我们还需要考虑出来后是否会反转决策权,那么:
- 当 \(sz_v\) 为偶数,\(sz_v - dp_v \ge dp_v\) 时,因为进入该子树再出来并不会改变决策权,并且该子树对后手是正贡献,那么这类子树一定是必选的。
- 当 \(sz_v\) 为偶数,但 \(sz_v - dp_v < dp_v\),这样的子树虽然不改变决策权,但是该子树对后手是负贡献,如果不是没得选,后手一定不选。
- 当 \(sz_v\) 为奇数时,出入该子树会改变决策权,那么选完第一类点之后,两个人都想交换决策权把第二类的负贡献点交给对面,所以都会一直选该类点直到选完,而后手一定会选 \(sz_v - dp_v - dp_v\) 最大的(即能拉开最大差距的点),于是我们贪心,模拟选点的过程即可。
根据最后的决策权,再把第二类点的贡献加上即可。代码。
CF1406D Three Sequences
流汗了啊,感觉可能这个性质并没有那么好发现吧/kk。首先注意到最大值肯定为 \(\max(b_n, c_1)\),考虑把这两个东西关联到一起,所以有 \(a_1=b_1+c_1=b_n - \sum_{i=2}^n(b_i-b_{i-1})+c_1\),显然当 \(b_n,c_1\) 差最小时最大值最小,那么直接最大值即为 \(\dfrac{a_1 + \sum_{i=2}^n(b_i-b_{i-1})}{2}\)。考虑修改,修改在差分上很好 \(O(1)\) 维护,于是我们直接做就行了。代码。
我错了,给唐神讲的时候发现这题前面的贪心性质居然没写,我自裁。有了前面的推导之后,现在的问题转化为求出 \(b\) 序列的差分数组。注意到这样一个性质,当 \(a\) 序列的数全部相同时,\(b, c\) 全部相同最优。当 \(a\) 递增时,把 \(b\) 全部分配给 \(b\) 最优;当 \(a\) 递减时全部分配给 \(c\) 最优,证明就考虑,比如说递增,如果全部分配给递减的 \(c\) 的话,\(c\) 需要整体抬高,但是给 \(b\) 就规避了。或者说,如果我们想要最小化一个递增序列的最大值的话,我们一定会想尽量最小化其中任意一个值,递减同理。
CF1804E Routing
考虑一个点只有一条出边,那么最终形成的一定是一棵基环树森林,又因为任意两点都需要连通,所以就是一棵内向基环树,注意到这个基环树的形状只能是一个环挂一些叶子,因为一旦挂的树深度大于 \(1\) 了,最外面的点是没办法被访问到的。
那么现在问题转化为,要在原图中找出一个上述形态的基环树,注意到环相关问题很喜欢断环成链,那我们不妨断环成链,只需要找出一条链,并且满足这个链的首尾相连,就构成了一个环,于是有 \(f_{S, u, v}\) 表示当前链上的点构成的集合为 \(S\),链的起点和终点分别为 \(s, t\),这样的链是否存在,转移简单,但是复杂度是 \(O(n^32^n)\)。注意到一个环相邻点都相连,但是我们的 dp 里把换上每一对相邻的点都算了一遍,而这些状态对应的环都是相同的,相当于对于每一种环都做了环长次 dp,于是我们可以直接钦定环上编号最小的点作为起点(当然,最大也可以),这样复杂度变为 \(O(n^22^n)\),输出方案直接记录前驱即可。代码。
Daimayuan3447 某道题的checker
赛时把性质分析出来了,但是根本不知道怎么维护。
考虑现在对于相邻的 \(a_i\) 和 \(a_{i+1}\),什么样的 \(x\) 能满足 \(a_i | x < a_{i+1}|x\),在二进制视角下,我们从高位向低位考虑,对于一位来说,当 \(a_i\) 和 \(a_j\) 相等时,这一位没有限制;当 \(a_i\) 为 \(1\) 且 \(a_{i+1}\) 为 \(0\) 时,这一位必须为 \(1\);当 \(a_i\) 为 \(0\) 且 \(a_{i+1}\) 为 \(1\) 时,可以直接填 \(0\) 然后后面无限制,也可以填 \(1\) 然后向后接着填。
如果我们将所有无限制的位置视为 \(0\) 的话,相当于对一个数的所有超集做出贡献。但注意到最后一种情况中,如果填 \(1\) 了并不是没贡献,而是需要接着向后填,所以我们容斥,让这位可以随便填,然后再减掉当前位置为 \(1\) 的情况。最后直接做高维前缀和统计答案即可。代码。
CF2112E Tree Colorings
先考虑逆问题,即给出一棵树,该树有多少美丽的染色方案,令 \(dp_u\) 表示 \(u\) 为绿时,该子树的染色方案数,那么一个儿子 \(v\) 有绿、黄、蓝三种染色,注意蓝黄一旦染色就要染一棵子树,那么三种颜色方案分别为 \(dp_v, 1, 1\),所以
叶子的 dp 值为 \(1\),那么显然所有的 dp 值都是奇数(因为是若干奇数的乘积),所以当 \(m\) 为偶数时一定无解。
考虑原问题,我们注意到方案数是乘积形式,那么如果我们有两棵树 \(x, y\),把 \(y\) 接到 \(x\) 下,方案数就是 \(dp_x(dp_y+2)\),这启示我们,既然是乘积形式,如果令 \(f_x\) 表示方案数为 \(x\) 的最小树大小,那么 \(x\) 可以从 \(x\) 的约数转移,实际意义就是拼接树,这样做为什么最优化呢?不能将 \(y\) 接到 \(x\) 子树里的任何一点吗?事实上当然可以,但是这种方案的依然包括在我们转移的所有因数中。
于是解法呼之欲出了,我们再整理一下转移:
就是把方案数拆分为 \(d, x/d\),而贡献了 \(x/d\) 实际上贡献的方案数是 \(x/d+2\),所以原方案数就是 \(x/d-2\)。
枚举倍数,得到复杂度调和级数。代码。
2025多校冲刺NOIP模拟赛3
最小字典序
题目链接。
先考虑一个朴素的暴力 dp,先有 \(dp_i\) 表示 \([i, n]\) 区间能获得最小的答案(把整个答案内容是什么都存下来),注意到因为是比较字典序,所以答案一定形如 \(a_i | a_{i+1} | ... | a_j + dp_{j + 1}\),这里的 \(+\) 表示拼接。因为任意比 \(a_i\) 大的数字在当前位置上一定都不如 \(a_i\) 优秀。那么实际上就是比较一堆 \(dp_{j}\),直接逐个和最优的比较,单次比较可以哈希加二分做到 \(\log n\),总的复杂度就是 \(n^2\log n\)。
但是,我们真的需要记下来所有的序列具体长什么样子吗,他们事实上都是在原序列上的某些段,那么再把二分换成等价的倍增,就有 \(fa_{x, i}\) 表示 \(dp_i\) 的答案中,第 \(2^j\) 位在原序列中的下标,\(h_{x, i}\) 表示 \(dp_i\) 中这一长度为 \(2^j\) 的段的哈希值。这样空间就是 \(n \log n\) 的了,虽然时间还没有贡献。我们继续观察,注意到并不是所有的转移都有意义,考虑一对转移点 \((i, j)\),当 \(i < j\) 且 \(dp_i \le dp_j\) 的时候 \(j\) 是没有价值的,因为越小的转移点,越能满足提到的 \(a_i | a_{i+1} | .. | a_j = a_i\) 这个条件。基于这个事实,我们用单调栈维护转移点,并在单调栈上二分,那么一个位置进入一次栈、再被弹出一次,总复杂度就变成 \(O(n \log n)\) 的了。代码。
在空无一物的时光深处
这种后面的操作,会覆盖前面的操作的这种问题,特别喜欢从后往前考虑来把操作导致的变化,变成钦定最终形态后,只能覆盖 \(0\),这种东西。这个题也是,我们从后向前考虑染色,然后现在染色就只能对 \(0\) 染色了。考虑 dp,最显然的想法设(我赛时的想法)\(dp_{i, l, r, j}\) 表示当前进行到了第 \(i\) 次染色,有颜色的区间是 \([l, r]\),当前笔在位置 \(j\) 的方案数。
这种 dp 不对,关键问题在于笔终点不在同一位置,但是方案可能是相同的,具体来说,假设当前染色段为 \([l, r]\),我们的笔在其中来回行动但是并不跨出边界,那么当前的方案全部相同,但是这样 dp 会让我们持续的转移。
那么该怎么 dp 呢?如果我们想彻底的规避这种在染色段内部转来转去的情况,我们不如直接钦定每次转移必须扩展边界,那么,我们有 \(dp_{i, l, r, 0/1}\) 表示当前进行第 \(i\) 次染色,染过色的区间是 \([l, r]\),当前在左端点/右端点的所有方案数。但是这样做起码也是 \(O(n^4)\) 的,我们还得再想想办法。注意到我们转移时其实只在意长度,换言之,对于任意起点的 \([i, i + len - 1]\) 答案全部相同,这意味着我们只需要把 \(l, r\) 压成一个 \(len\) 即可。于是现在的状态变成了 \(dp_{i, len, 0/1}\),考虑转移。可以向同侧转移也可以向异侧转移,但是异侧的条件是执行 \([k, i]\) 的所有颜色后,能够到达对应点,这个可以 bitset 维护,最后的复杂度是 \(O(n^2m^2)\)。代码。

浙公网安备 33010602011771号