和主席 VP 玩了一下,打了 8 题,只能说主席太强了。
比意想中的要简单一点,而且有两道随机化题。
A - Atomic Energy
考虑一个暴力 dp:设 (f(i)) 表示中子数为 (i) 的原子可以产生的最小能量。那么在 (M) 以内的 dp 值是可以 (O(M^2)) 预处理的。
注意到 (k) 很大,而 (n) 很小。考虑背包问题的一个假贪心:每次选“性价比”小的。一般情况下,组合起来可能不是最优的。然而当我们的容量很大时,我们可以在一个限度内施展这样的贪心。由于每个物品的体积都比较小,所以“组合更优”不会在大范围出现。
对于本题,设阈值 (M=n^2)(这个值不一定最优,是平方算法可以承受的较大值)。然后 (O(M^2)) 预处理小范围的 dp,对于 (k) 很大的情况可以先一直一个性价比最小的,直到在 (M) 以内直接拼上 dp 值。这样复杂度是 (O(M^2+q)) 的。
C - Contest Struggles
签到。答案为 ((d imes n - k imes s) div (n-k))。在 ([0,100]) 内合法。
D - Dragon Balls
限制很宽,有很多种做法。
这里是个好写的做法:考虑随机一个点,算出距离 (d^2)。考虑这个 (d^2) 肯定是根据勾股定理算出来的,那么我们只要得到 (a,b) 两个整数满足 (a^2+b^2=d^2),然后在随机的那个点周围 (8) 个方向上都测一遍。
实测这样得到的 ((a,b)) 最多三十多组,并且随机情况组数很少。所以一千次是绝对足够的。
(d^2) 可以达到 (10^{12}) 级别,但 (a, b) 只有 (10^6) 级别,所以不会有超时问题。
E - Endgame
首先考虑一个简单问题:给定始终点 (A, B),判断 (A) 能否操作两次吃掉 (B)。这个比较简单,用一个 set 记录所有操作,然后枚举第一次操作,尝试配对所求第二次操作。这样可以 (O(nlog n)) 完成一次判断。当然可以强行优化到更快。
实现上面从操作之后可以判断 Alice 是否能赢。但是之后是否存在 tie 的位置似乎没什么好办法,对于 (n) 比较小我们可以逐一测试,但是肯定是过不了的。
我们发现一个很离谱的事情就是,棋盘是 (n imes n) 的,而操作种类是 (n) 个。为什么都是 (n)?(n) 个操作可以拼出 (O(n^2)) 个位置,但绝(除了 (n=2,3) 的小数据)不会占满整个棋盘,而且冷静分析有相当一部分位置都是空的。
我们考虑随机化:每次随机一个点,判断是否可以 tie。由于随机很多次都点不到的概率是极低的,所以很快就能找到解。
官方题解指出空位的规模是 (n^2/2),因此期望两次出解。但是我不知道怎么来的,一个较简单的 (n^2/4) 的构造是 ((1, 0), (2, 0), cdots, (n/2, 0), (0, 1), (0, 2), cdots, (0, n/2))。
F - Flight Collision
要求快速模拟整个过程,实际上只需要我们正确模拟就行了,因为每个坠毁时间只会在一对飞机上发生一次。
要正确模拟,只要让一个个坠毁事件按时间顺序发生,显然这样不会出现任何跨越的情况,其间飞机间的相对位置也不会改变。
考虑用 set 或链表维护存活的飞机,用堆维护接下来可能发生的坠毁事件。复杂度 (O(nlog n))。
G - Great Expectations
完全不会这种带环的期望题。
从容错的角度考虑状态设计:我们需要比 (r) 小,而 (n) 是不得不消耗的花费,那么最多可以承受 (r-n-1) 的失误代价和。设 (f(i,j)) 表示将要到达第 (i) 个关卡,以及消耗了 (j) 的容错,走到终点需要的期望时间。
可以发现的是,如果真的要重开,那一定是刚刚失误被卡了一下的那个时刻。那么有方程:
其中 (f(0,0)) 是起点的期望,代表重开,同时也是我们要求的答案。
我们试一个答案 (v),并在方程中用 (v) 代替 (f(0,0)) 计算,并用最后得到的 (f(0,0)) 比较。如果 (f(0,0)) 较大说明答案会比 (v) 更大,反之同理。我也不知道为什么会这样。
最后二分答案即可。复杂度 (O((r-n)m log ext{ans}))。
H - Hot Springs
把所有数组放到一个数轴上。可以发现,我们从中间位置开始,左右横跳,越跳越远,这样必然可行。
那么我们找到中位数,然后分成两部分,轮流输出即可。
I - Island Tour
考虑 (O(n^4)) 暴力,枚举三个人的位置 (i, j, k),然后检查每个位置是否都不冲突。如果 (i, j) 两个人中 (j) 先到某位置 (p),那么 (j) 离开 (p) 的时间不应大于 (i) 到达 (p) 的时间。
VP 时主席写了个这样的暴力,然后加了个超过时限就输出无解摆烂,结果真的 AC 了。之后猜了猜,如果有解,那么 (i=1) 时也必然有解,结果加个 assert 也过了。这玩意也不知道怎么证,也可能是假的。如果有人能给个证明可以在 这里 或者评论区说两句。
但其实靠谱做法非常简单:观察到我们要判的只有 (O(n^2)) 种本质不同的位置对,那么只有 (O(n^3)) 预处理是否合法即可。最后求解也是 (O(n^3))。
J - Joint Excavation
很好玩的题。官方解法也非常妙。
考虑直接 DFS。DFS 过程中的点我们分为三类:(一)从未访问的点,(二)DFS 栈中的点,以及(三)曾经访问过但不在栈中的点。曾经访问过就是经历过回溯操作的点。
一个神奇的观察:三类点中,(一)类和(三)类间不相邻。具体证明非常显然:考虑 DFS 的过程,如果存在这条边,那么当时不可能直接回溯而会直接进入那个未访问的点。
注意(二)类点刚好可以构成一条点不自交的路径,那么最后在 DFS 中某时刻出现(一)类点和(三)类点数目相同时遍求得了一组解。
K - Keyboardd
检查所有小写字母及空格,把多的输出即可。