久违的绿色啊……上次看到这个已经是 14 个月之前了/kk
A
直接根据奇偶黑白染色。
时间复杂度 \(O(nm)\).
代码: 119943026
B
显然我们可以用 \(1\) 次操作将 ugliness 减少 \(2\) 当且仅当操作了一个比两边都大的数,不然操作次数一定大于等于 ugliness 减少的数量。于是直接贪心即可。
时间复杂度 \(O(n)\).
代码: 119943269
C
考虑假设让第一行的数 \(i\) 所在列上下交换了,那么该列第二行的数在第一行对应的列也要交换,这个交换还要继续导致某些列要交换,以此类推。
对于每一列,把这一列的两个数连一条边。那么不难发现,每个连通块必须作为一个整体出现(换或者不换),答案就是 \(2\) 的连通块个数次幂。
时间复杂度 \(O(n)\).
代码: 119943760
D
考虑先问出 \(1\) 号点到每个点的距离,然后按奇偶分类,在距离是偶数和距离是奇数的两个集合中挑一个较小的全问一遍。
这样就能做到询问次数不超过 \(\lceil\frac{n}{2}\rceil\),时间复杂度 \(O(n^2)\).
代码: 119944573
E
首先可以把问题转化一下:就相当于每次覆盖一个 \(k\) 元不可重子集,要求尽可能少的操作次数使得 \(1\) 至 \(n\) 每个数都被覆盖了奇数次。
于是立刻可以得到,当 \(k\) 是偶数而 \(n\) 是奇数时无解。
做法一(我的做法)
考虑求询问次数 \(q\) 的最小值。由于数据范围很小,不妨枚举 \(q\),然后判断是否合法。
则合法必须要满足以下两个条件:
(1) \(kq\equiv n(\mod 2)\)
(2) 询问次数最大的一个数的询问次数不超过 \(q\). 即,\(n\) 个和为 \(kq\) 的奇数,最大的一个数的最小可能值不超过 \(q\). 这一点很容易用除法判断。
并且我们不难发现,如果满足了上面两个条件,那么总是合法的——只需要每次记录下每个数要被问多少次,取出剩余次数最大的 \(k\) 个进行询问即可。
于是我们得到了一个求最优解和构造方案的策略。按此策略询问即可,时间复杂度 \(O(n^2)\) 或 \(O(n^2\log n)\).
做法二(题解做法)
一个大胆暴力而疯狂的想法——"It's pure BFS!"
考虑建立一个状态:“当前被覆盖奇数次的集合是 \(S\)”,然后可以得到状态之间的转移关系,BFS 可以求出最短路。
注意到状态其实只和 \(S\) 的大小有关,所以就压成了只有 \(O(n)\) 个状态,直接 BFS 即可。
代码: 119946668
F
根据题目中的关系,显然可以给所有的沙子建出一张有向图,一条边表示起点下落会导致终点跟着下落。注意到直接连会有 \(O(n^2m)\) 条边,但是每个点只需要往它正下方、左边一列的下方、右边一列的下方最高的点连边就可以了,所以边数优化到了 \(O(nm)\) 级别。
对这张图跑 tarjan 缩点,然后 F1 就做完了,答案等于缩点后入度为 \(0\) 的点数。
对于 F2,注意到只有第 \(i\) 列从下往上数第 \(a_i\) 块沙子(缩点之后所在的点)是关键的,我们的目标可以简化成用尽可能少的点来到达所有关键点。
让我们先把能到达其他关键点的点去掉,剩下的关键点按行编号排序。然后神奇的事情发生了:每个能到至少一个关键点的点能到的关键点是一个区间!
然后问题就变成了求在所有的区间中选出尽可能少的来覆盖全集,这是一个简单贪心问题,可以直接排序解决。
时间复杂度 \(O(nm+m\log m)\).
代码: 120521766
G
考虑假设固定了一条折线,一个目标点会在哪里被算进答案:切比雪夫距离相当于从一个点开始画一个不断往四边扩张的正方形,直到正方形和折线有交为止。那么一旦扩张到某个边长时有交了,如果这个点在折线左上方,那么正方形右下角一定在折线上;否则左上角一定在折线上。换言之,一定是左上角或者右下角最先碰到折线,即折线上离这个点最近的点一定和这个点位于同一条斜率为 \(-1\) 的直线上。
于是可以看作每个点属于某一条斜率为 \(-1\) 的直线,点之间就有了顺序,可以写一个 \(O(nW)\) 的 DP,设 \(f[i][j]\) 表示扫描到第 \(i\) 条线,当前位于这条线上的 \(j\) 位置。
进一步发现,最开始 \(i=0\) 时所有的 DP 值都是 \(0\),每次跳到下一个点的操作相当于先把每个位置的 DP 值变成它和它之后连续若干个元素的 \(\min\),然后再加上一个绝对值函数。
那么在加入前 \(i\) 个点时这是一个有 \(O(i)\) 段的分段一次函数,且它的斜率是不降的。
考虑维护这个函数每个斜率变化 \(1\) 的横坐标,每次要做的就是:找到斜率为 \(0\) 的位置(求第 \(K\) 大)并将其后面的元素全部增加某个数(后缀加)、找到某个坐标并插入两个分界点(插入)。直接做看起来要用平衡树之类的东西。
但是!!注意到大部分操作都是和斜率为 \(0\) 的位置相关的,这启发我们维护斜率为 \(0\) 的位置。使用两个堆来分别维护左侧斜率小于 \(0\) 的部分和右侧斜率大于 \(0\) 的部分,这样后缀加就变成了全局加,非常易于实现。
最终计算答案的时候,可以随便找一个无穷远处的位置算出来代价(相当于求出了分段函数第一段的截距),然后依次遍历分段函数把增量算上。
上述维护分段函数的套路其实有个名字:Slope Trick.
时间复杂度 \(O(n\log n)\).
代码: 121115828
H
这又是一道假交互题=-=
主要问题在于求最少询问次数,显然是要使用 DP 解决。
因为已知一个点 \(rt\) 在 \(a\) 到 \(b\) 的路径上,那么 \(a\) 和 \(b\) 一定在 \(rt\) 的两个不同子树里,于是单看某个子树里问题就变成了“有一个未知的点,每次可以询问它和一个点的 LCA”。
设 \(f[u]\) 表示要确定 \(u\) 子树内的一个点,最坏情况下最少要询问多少次。一个很自然的想法是转移枚举第一个询问的是子树里的哪个点,然后分成好多棵子树去处理——但是这种思路其实不太合适,因为枚举一个点再枚举它到根路径上每个点的 DP 值会带来很高的复杂度。不妨这样考虑:我们直接枚举第一个询问的是哪个子树里的点!设这个子树是 \(c_0\),如果答案在其中,那么付出的代价是 \(f[c_0]\). 否则,我们花费了 \(1\) 次询问排除了这个子树,于是代价等于剩下部分的 \(f\) 值 \(+1\). 对于剩下部分的 \(f\) 值,我们继续枚举第一个询问的是哪个子树里的点……由此思考下去,可以发现其实我们在确定一个询问的顺序 \(c_0,c_1,...,c_{k-1}\),而最终的 \(f\) 值就等于 \(\max(f[c_0],f[c_1]+1,f[c_2]+2,...,f[c_{k-1}]+k-1,k)\),我们希望最小化这个值。所以这个顺序就应该是按 \(dp\) 值从大到小排序,这样就完成了转移。
对于根节点处的统计答案需要特殊考虑,因为根节点可能会有两个子树中有关键点。同理可得根节点的答案等于 \(\max(f[c_0]+k-1,\max_{i<j} (f[c_i]+f[c_j]+j-1))\). 注意到由于 \(f\) 值是从大到小排序的,所以 \(i=0\) 时一定取到最大值,式子就变成了 \(f[c_0]+\max(f[c_1],f[c_2]+1,...,f[c_{k-1}]+k-2,k-1)\).
然后由于这道题要对所有的 \(rt\) 求出答案然后取最大值,所以需要换根 DP 一下,使用维护前缀后缀 \(\max\) 的常见套路即可优化到 \(O(n\log n)\).
交互其实就是强行输出方案,按照 DP 求出的顺序询问即可。
时间复杂度 \(O(n\log n)\).
代码: 121412860