牛客练习赛 136 DEF 题解
牛客练习赛 136 DEF 题解
D. 幽幽子的魔法宴会
贪心,[绿]
设选择施用魔法的食物集合为 \(S\)。由于 \(S\) 和 \(x\) 都是变量,无从下手。
考虑确定 \(S\)。设 \(x\) 的 highbit 为 \(i\),那么 \(j \in S\) 当且仅当 \(a_{j}\) 的第 \(i\) 位为 \(1\)。这是运用了二进制贪心的经典结论:\(\sum\limits_{k = 0}^{m - 1} 2^{k} = 2^{m} - 1 < 2^{m}\),也就是说尽可能让高位为 \(1\) 总是最优的。
确定 \(S\) 之后就好办了。接下来可以逐位确定 \(x\) 的低位,而各位是互不相关的。对于第 \(k\) 位,统计 \(S\) 中第 \(k\) 位为 \(1\) 和 \(0\) 的数量,如果 \(0\) 比较多,则令 \(x\) 的第 \(k\) 位为 \(1\) 可以使总和增大。选出所有能使得总和增大的位以后,先判断最大的总和能否达到 \(y\),如果可以,再最小化代价。还是运用那个结论:从高到低枚举选择了的位,如果删去之后还能达到 \(y\),那么就可以删去。
枚举 highbit 再计算,即可解决本问题。设 \(w = \max a_{i}\),则时间复杂度为 \(O(n \log^{2} w)\)。代码
E. 纯粹的退治
分块,[紫-]
比较常规的 ds 题,稳扎稳打。
根据 WC T3 经典结论,最小操作次数就是 \(\sum\limits_{i = 1}^{n} \max(a_{i} - a_{i - 1}, 0)\),其中 \(a_0 = 0\)。
然后我们应该考虑原数组的差分。因为:
- \(a_i - a_{i - 1}\) 本身就是差分的形式。
- 使区间加上一个等差数列,在差分数组上体现为使区间加上一个常数,这方便我们刻画问题。
那么设 \(b_i = a_i - a_{i - 1}\),则修改操作就是:使 \(b\) 数组中 \([p - x + 1, p]\) 中的元素 \(-1\),\([p + 1, p + x]\) 中的元素 \(+1\)。查询操作就是求 \(b\) 中所有正数的和。
可以想到用线段树/分块等数据结构维护这种区间问题。但我们可以证明:这个问题的时间复杂度不能做到 \(O(n \operatorname{polylog}(n))\)(见文末),所以只能分块。实际上就算无法证明这个结论,也应该发现线段树难以维护信息。
考虑对区间 \(+1\) 之后,区间正数和 \(\operatorname{sum}\) 的变化。设 \(\operatorname{cnt}_{\ge x}\) 表示区间内 \(\ge x\) 的数的个数,则区间 \(+1\) 以后,每个大于等于 \(1\) 的数都产生 \(1\) 的贡献,因此 \(\operatorname{sum} \gets \operatorname{sum} + \operatorname{cnt}_{\ge 1}\)。类似地,区间 \(-1\) 以后,\(\operatorname{sum} \gets \operatorname{sum} - \operatorname{cnt}_{\ge 0}\)。
如何维护 \(\operatorname{cnt}\) 呢?我们先记录每块的整体偏移量 \(\Delta\),则区间中 \(\ge x\) 的数实际上是 \(\ge x - \Delta\) 的数。那么我们可以开一个桶来维护区间内 \(\ge x\) 的数的个数。但是值域很大,无法开下整个桶。我们设一个阈值 \(m\),仅维护 \([-m, m]\) 中的 \(x\) 的 \(cnt\)。当 \(\Delta < -m\) 或 \(\Delta > m\) 时,就重构整个块,把 \(\Delta\) 的影响施加到 \(b\) 上,把 \(\Delta\) 清零并重新求出 \(cnt\)。设块长为 \(B\),则重构整个块的时间复杂度为 \(O(B + m)\)。由于每次修改只会 \(+1\) 或 \(-1\),所以因为 \(\Delta\) 超出阈值导致的重构次数应该不会很多。
于是我们的解法就很明显了:修改时,对于整块,修改 \(\Delta\) 和 \(\operatorname{sum}\),如果 \(\Delta\) 超出阈值 ,就重构整个块。对于散块,则必须重构整个块(因为 \(\operatorname{cnt}\) 本质上是后缀和数组,除了整个重构没办法快速修改)。查询时,求所有块的 \(\operatorname{sum}\) 的和即可。
时间复杂度分析:查询的时间复杂度一定是 \(O(n / B)\),主要看修改操作。修改时,一次重构两个散块,时间复杂度 \(O(B + m)\);修改整块时,不考虑 \(\Delta\) 超出阈值导致的重构,时间复杂度为 \(O(n / B)\)。由于每次修改只会 \(+1\) 或 \(-1\),所以每修改至少 \(m\) 次,一个块才会因为 \(\Delta\) 超出阈值重构一次。考虑全部 \(q\) 次修改,则总重构次数为 \(O\left(\dfrac{qn}{mB}\right)\)。因此总时间复杂度为 \(O\left(q \left(B + m + \dfrac{n}{B} + \dfrac{(B + m)n}{mB} \right)\right)\),取 \(m = B = \sqrt{n}\) 即可做到 \(O(q\sqrt{n})\) 的时间复杂度。
实际上重构的次数可能没有那么多,我代码中取 \(m = 40\),目前只有三个人跑的比我快。代码
关于该问题时间复杂度不为 \(O(n \operatorname{polylog}(n))\) 的证明:
先考虑这个问题:给定长为 \(n\) 的初始序列 \(a_i\),进行 \(O(n)\) 次区间 \(+1\) 或 \(-1\) 的操作,\(O(n)\) 次查询区间中 \(0\) 的个数。如果这个问题能做到总时间复杂度 \(O(f(n))\),则矩阵乘法可以做到 \(O(n \cdot f(n))\)。
再修改一下限制,改成区间加任意整数。记 \(m = \sqrt{n}\),设有两个 \(m \times m\) 的 \(01\) 矩阵 \(A\) 和 \(B\),要计算 \(C = A \times B\)。构造一个长度为 \(n\) 的序列 \(a\),把它均分成 \(m\) 段。然后从 \(A\) 的第 \(j\) 列构造 \(a\) 的第 \(j\) 段:一开始 \(a\) 全为 \(0\),如果 \(A_{i, j} = 1\),则把第 \(j\) 段中任意一个 \(0\) 改为 \(i\)。计算 \(C_{x, y}\) 时,先令 \(a\) 中所有元素 \(-x\),则第 \(k\) 段中存在 \(0\),当且仅当 \(A_{x, k} = 1\)。然后再对于所有使得 \(B_{k, y} = 0\) 的 \(k\),令第 \(k\) 段中所有元素减去一个大于 \(m\) 的数,这保证了第 \(k\) 段中不会存在 \(0\)(相当于“屏蔽”一个块)。此时 \(C_{x, y}\) 就是 \(a\) 中 \(0\) 的个数。(可以这样理解:令 \(a\) 中所有元素 \(-x\) 筛选出了第 \(A\) 的第 \(x\) 行中的 \(1\),而第二步操作筛选出了 \(B\) 的第 \(y\) 列中的 \(1\)。操作以后 \(a\) 的第 \(k\) 段存在 \(0\),当且仅当 \(A_{x, k} = 1 \and B_{k, y} = 1\),所以每个 \(0\) 对 \(C_{x, y}\) 贡献 \(1\)。)
先枚举 \(y\),进行 \(O(m)\) 次区间加操作,对使得 \(B_{k, y} = 0\) 的 \(k\),令第 \(k\) 段中所有元素减去一个大于 \(m\) 的数。然后再枚举 \(x\),对每个 \(x\) 进行一次全局 \(-x\) 操作,再全局 \(+x\) 复原,总操作 \(O(m)\) 次。枚举所有的列,则总操作 \(O(m^{2}) = O(n)\) 次,因此计算整个 \(C\) 的时间复杂度是 \(O(n \cdot f(n))\)。
然后考虑区间 \(\pm 1\) 的问题。由于 \(x\) 连续枚举,所以每次令 \(x \gets x + 1\) 时只需做一次全局 \(-1\) 操作。现在没法减去一个大于 \(m\) 的数了。我们把上述的所有操作都做两遍,这样序列中的元素都是偶数。为了屏蔽一个块,只需区间 \(+1\) 即可,因为 \(0\) 不是奇数。
综上所述,证毕。已知暴力矩阵乘法的时间复杂度为 \(O(m^{3}) = O(n^{1/2})\),所以这个问题的时间复杂度应该不会快于 \(O(n^{1/2})\)。(否则我们就有更快的矩阵乘法了)
剩下的待补
F. 斯卡雷特家的游戏
博弈,Ad-Hoc,[蓝]
称先手为 Alice,后手为 Bob。
神秘 Ad-Hoc 题。
结论很简单,我们追求的是有迹可循地想出答案。
先考虑比较简单的情况。
\(n = 1\) 时,显然先手必胜。
\(n = 2\) 呢?不妨假设 Bob 指定一堆。如果 Alice 取走整堆,则只剩下一堆。轮到 Bob 操作的时候,Bob 就可以取走剩下的一堆,那么 Bob 获胜。所以 Alice 不应该取走整堆,只有一种例外:存在堆的大小为 \(1\) 时,Alice 只能取走整堆。因此,如果存在一堆的大小为 \(1\),则 Bob 必胜。不存在大小为 \(1\) 的堆时呢?关键的来了:Alice 可以把某一堆的大小取到恰好为 \(1\),这样就转化到了之前的情况。因此,如果 \(n = 2\),当且仅当不存在某一堆的大小为 \(1\) 时先手必胜。
对于 \(n\) 更大的情况,考虑能不能转化到已知的情形。使用数学归纳法,假设对于 \(n \le k\) 时上述结论都成立,则当 \(n = k + 1\) 时,如果存在一个 \(1\),那么 Bob 指定 Alice 取走所有的 \(1\),就转化到了 \(n\) 更小,但是不存在 \(1\) 的情况。根据归纳假设,这种情况 Bob 必胜。(特别地,如果所有堆的大小都为 \(1\),则 Bob 指定 Alice 取走 \(n - 1\) 个 \(1\),就转化到了 \(n = 1\) 的情况,还是 Bob 必胜。)否则如果不存在 \(1\),则 Alice 可以把某一堆的大小取到恰好为 \(1\),这就转移到了先手必败的局面,因此 Alice 必胜。
综上所述,该结论当 \(n \ge 2\) 的时候都成立:当且仅当不存在某一堆的大小为 \(1\) 时先手必胜。这道题就做完了。
时间复杂度 \(O(n)\)。AC 记录