2020 ICPC 上海赛区题解

A Wowoear

假设已经找到了答案的 ab 两个点,我们可以尝试沿着原有路径移动点 a。因为 ab 是最优解,两个移动的方向都要(使得答案变差或者使得 ab 的线段不合法)。如果有一个方向移动答案不变差且不会变得不合法,那就可以一直沿着那个方向移动,直到答案会变差或者马上会不合法。这两种情况都意味着 ab 的线段碰到了原有折线的某个拐点。所以答案的 ab 线段上一定要有一个原来的拐点。

有一个拐点以后,要么以它为中心旋转 ab 连线到一个极小值点(把以拐点为视点看到的线段极角排序,分段三分),要么有两个拐点(枚举这两个点,连线,找到这条直线和原来折线的交点,处理这些交点之间抄近路的答案。抄近路要求不能穿过原来的折线,也不能碰到原来的折线。但是有些碰到的情况可以通过左右微调解决)。

B Mine Sweeper II

考虑数字和就等于相邻(雷格子,非雷格子)二元组的个数,于是把整个地图全反过来这个二元组个数不变,然后 BA 之间以及 Binv(A) ,即把 A 所有格子全部取反的扫雷地图之间总有一个偏差不超过一半的,所以就选其中偏差不超过一半的然后变过去就行。

C Sum of Log

考虑数位 DP。设 Dp[i][state] 表示当前考虑到第 i 位,state 包含的状态有:当前 x 是否卡住 X 的上界、当前 y 是否卡住 Y 的上界、当前数字是否为 0,在这基础上的答案。然后从高到低枚举 i,再枚举 x,y 在第 i 位上的状态,可以维护出后继状态,然后转移即可。

D Walker

大概来说,我们可以分几类来考虑这个问题:

1. 一个人把所有地方走完;

2. 两个人对穿,也就是说他们互相往另一个人的方向走,直到边界;

3. 两个人各负责自己的一边,然后在中间相遇,那么对于每个人,有两种选择,一种是先往端点走,一种是先往中间走,那么我们可以二分时间,就能够算出每个人往中间最多能走多少路,判断一下能不能把线段覆盖了就行啦!

E The Journey of Geor Autumn

可以先尝试找规律做出此题,有公式后再思考如何证明。

考虑 1 在哪个位置。一定要在前 k 个位置之一。枚举这个位置 x。整个序列合法当且仅当 1 之前任意放置,xn(里面的数字离散化后)形成一个长度为 n-x+1 的合法序列。

上述递推式可以优化到线性。

F Fountains

先考虑所有 weight=1 的情况。把每个区间 (L, R) 看成平面上 x = L, y = R 的一个点。一个区间包含另一个即前者在后者左上方。那么可以斜角按 R - L 由大到小 dp, 维护考虑了 n*(n+1)/2 个区间中的前 i 个时,选择了 j 个区间,并且没有被覆盖到的区间形成的边界线是 n \ge a1\ge a2 \ge ... \ge an \ge 0 时的花费。边界线总情况是 2nn , 总共的state 大概是 (n(n+1)/2)^2 * C(2n, n) 。转移可以 O(n) 实现。

加上 weight 之后,实际是考虑点 prefix_{L-1}prefix_{R}, 实现时只需要按照区间 (L,R) 的 weight 排序,由大往小做即可。

G Fibonacci

考虑斐波那契数列模 2 以后的值 gg[3k] = 0,g[3k+1]=g[3k+2]=1,我们只需要计算一下 1...n 中有几个奇数,有几个偶数,就可以算出答案啦。

H Rice Arrangement

有一个结论是:我们把每个人和对应的手抓饭在圆盘上连线,那么这些连线是不会相交的。

这样我们可以直接枚举第一个人对应的手抓饭编号,然后转一圈计算答案。复杂度 O(N^2\log N)\log 是排序。

结论的证明如下:

随便固定圆上一个方向为左(例如顺时针),另一个方向为右。枚举了往左和往右转多少以后,每碗手抓饭都在一个区间上出现。固定一组可行解。把人画在大圈上,手抓饭画在小圈上,人 i 吃了手抓饭 j 就在他们之间连一条边。边要画在两个圆之间手抓饭 j 覆盖的区间内。每个手抓饭覆盖的区间在这碗手抓饭左边的成为左臂,右边的称为右臂。所有 k 条边两两之间要避免不必要的相交,即仅当(1)两条连线的区间有交且连线为一个人连接饭的左臂另一个人连接另一个饭的右臂,或者(2)两条连线的区间为包含关系时,两条连线才有交。

假设人 i 吃手抓饭 j 连的边和人 k 吃手抓饭 l 的边有交。不妨假设人 i 在饭 j 的左臂里(否则镜像)。

假如人 k 在人 i 到饭 j 连线的区间里(此时人 k 在饭 j 的左臂)。假如人 k 在饭 l 的左臂里,为了两条连线有交,饭 l 也在人 i 到饭 j 连线的区间里。这样因为每份饭的左臂长度一样,人 i 也在饭 l 的左臂里。现在连接人 i 和饭 l,人 k 和饭 j,交点没有了。

假如人 k 不在人 i 到饭 j 连线的区间里。假如人 k 在饭 l 的左臂。此时和上面情况一样,只是人 i 和人 k 名字换了,饭 j 和饭 l 名字换了。假如人 k 在饭 l 的右臂。此时人 k 也在饭 j 的右臂,因为把饭 l 的右臂沿着饭 l 到饭 k 的连线向人 k 移动(向右),移动到两条连线的交点,再移动到饭 j (依然向右),就是饭 j 的右臂,此时此右臂右端点已经超过人 k,左端点还未超过人 k。假如人 k 到饭 l 的区间包含人 i,此时我们可以交换人 i 和人 j 吃的饭使得连线不再相交。假如人 k 到饭 l 的区间不包含人 i,则饭 l 在人 i 到饭 j 的区间内。此时饭 l 的左臂必然包含 i,因为把饭 j 的左臂沿着饭 j 到人 i 方向(向左)移动至交点再向左移动至 l 即为饭 l 的左臂,此时左臂左端点已经超过人 i 且右端点还未超过人 i。这种情况也可以交换人 i 和人 k 吃的饭。

在以上所有情况里的交换都不会导致 i,j,k,l 之间的两条连线和其他 k-2 条连线的交点新增。(因为只是把原来两条线从他们之间的交点切开一点点。然后还有可能有连线绕远路被简化。)

综上我们总能找到一种方法保证解的可行性同时减少一个交点。因为交点个数是有限的,必然存在一个没有交点的可行解。

I Sky Garden

介绍一种 O(n^3) 的做法,考虑从里到外第 i 层,这层的每个节点都是一样的,所以我们统计其中一个对答案的贡献即可。那么对于第 i 层的一个点,它到小于第 i 层的点的最短路的第一步,一定是它往里走一层(在外面走圆环肯定比里面距离远);它到第 i 层的点的最短路,有两种情况,一种是直接走这层的圆环,另一种是往里先走一步,然后里面走一走,再钻出来,也就是说我们可以用第 i-1 层的值来更新第 i 层的值,dp 一下可以做到 O(n^3)

这题其实可以直接 O(1),大致来说就是往里走的每一层其实省的距离都是一样的,大家可以自己悟一悟!

J Octasection

枚举平行于 y 轴和 z 轴的平面(下称平面 x)的位置。和平面 x 有交的矩形就不用考虑了,和它无交的矩形就必须和平面 y 和平面 z 之一有交。平面 x 在移动的过程中,即平面 xx 坐标在从 -inf 变化到 inf 的过程中,每个矩形和平面 x 有交的时间是连续的一段。在这段时间外,这个矩形必须和平面 y 或平面 z 有交,即 ymin \le y\le ymax || zmin\le z\le zmax ,其中 ymin 为矩形 y 坐标最小值, ymax 为矩形 y 坐标最大值, zminzmax 类似, y 为平面 yy 坐标, z 为平面 zz 坐标。画在 x 平面上,就是 y 平面在 x 平面上的投影(一条平行于 y 轴的直线)和 z 平面在 x 平面上的投影(一条平行于 z 轴的直线)的交必须在以矩形为中心的红十字形状内。也就是二维版的本问题。

在二维版的本问题中,要判断的其实是每个矩形对应的红十字形状的交是否为空。我们取红十字形状的补集,即左上,右下,左下,右上 4 个不封闭矩形区域。所有矩形的左上区域的并构成了左上的一条轮廓线,其它 3 个角类似,这 4 条轮廓线外的区域即为所有红十字形状的交。如果要判断交是否为空,可以先将 yz 坐标的范围限制为 [-M,M]M 为本题坐标范围),为每个 y 坐标计算该 y 坐标上(上 = on, 不是 above)有多少 z 坐标位于轮廓线外。对于一个 y 坐标,在这个 y 坐标上的 z 坐标其实有两个下界和两个上界,分别由左上左下右上右下轮廓提供,下界取较大的生效,上界取较小的生效。

在三维版本的问题中,我们要维护上述 4 条轮廓线,同时维护每个 y 坐标上有多少 z 坐标位于轮廓线外。我们将所有 y 坐标按照 z 坐标的下界和上界由谁提供分为 4 类(下界可能由左上或者左下轮廓提供,上界则由右上或者右下轮廓提供)。每一类用一棵线段树维护上下界的差。为了方便每一类的线段树都包含每个 y 坐标。线段树支持查询是否有大于 0 的元素。当轮廓线修改时,每段修改可用线段树区间加操作实现(等下会讲有多少修改)。由于轮廓线都是单调的,肯定 y 坐标较大的部分由左上提供下界,较小的部分由左下提供。如果我们能维护一个轮廓线端点的 set,则可以在 set 上二分出左上和左下轮廓相交的位置。右侧类似。这样我们就得到了每一类的 y 坐标区间。在区间上,在对应类别的线段树里查询是否有非 0 位置即可。

最后是轮廓线端点的 set 维护。一个点在轮廓线上出现或消失的事件只能有常数个:它自己的长方体被平面 x 扫过的开始和结束是两个事件。它可能被别的轮廓线端点挡住,仅在挡住它的轮廓线端点消失时能露出来。所有挡住它的轮廓线都消失的 y 坐标的集合是一个区间(因为每个轮廓线端点消失的时间都是个区间,取这些区间的交),这个区间也只能生成 2 个事件。所以一个点在轮廓线上出现或消失的事件只能有常数个。我们只要把这些事件处理出来,到对应的时间在 set 中加入或删除对应端点即可。加入和删除的时候要更新 4 棵线段树。

K Traveling Merchant

观察发现,一旦能走到一个已经访问过的点,则可以模仿这个点之后,上一次使用的路径并无限重复。这样的路径在最开始的图中,是一条 LH 相间的路径,并且路径的最后一个点和路径中的某一个同色(同 L 或同 H)点有边。

使用异色边(原图中连接 HL 的边)建图,对于每条同色边 (u,v) 检查是否存在 0 -> u -> v 或 0 -> v -> u 的简单路径,如果有则存在一条上述的无限重复路径。设 uv 在 dfs 树中的 lca 为 w(不妨假设 w 不为 uv),存在这样的路径等价于 u(或 v)和 w 在同一个点双连通分量内。

L Traveling in the Grid World

如果 gcd(m,n)=1,那么我们可以直接通过去。

有个简单的结论是,如果 gcd(m,n)>1,我们只需转折一次即可。那么如何确定转折点呢?

假设转折点是 (x,y),我们希望 \sqrt{x^2+y^2}+\sqrt{(m-x)^2+(n-y)^2} 最小,同时两端线段的 gcd 都是等于 1 的。有一个性质表明,假设我们先最优化这个距离和,得到的解一定也是 gcd 满足要求的。这样的话,我们只需暴力枚举坐标去确定最近的点即可。

一个简要的证明:假设起点是 A,终点是 B 。我们找到不在 AB 连线上的使得 AC+BC 最小的一点 C。假如 AC 上有其他整点 D,且 AD+BD 小于 AC+BC。那么 AC 上没有其他整点,同理 BC 上也没有其他整点。又因为 ACBC 不同向(因为 C 不在 AB 连线上),所以 C 是问题的答案。

M Gitignore

当一个文件夹内(包括所有子文件夹内)所有文件都需要排除时,则可贪心地排除这个文件夹;否则单独排除直接位于这个文件夹内需要排除的文件,并递归求解子文件夹。

编辑于 2020-12-14 18:29