「这确实不好玩」:2021年ICPC南京区域赛命题心得及比赛题解

很荣幸,同去年一样,2021年ICPC南京区域赛依然由我们SUA程序设计竞赛命题组进行命题,感谢主办方南京航空航天大学,感谢邹春然老师 @邹春然 对我们的信任与支持。

数年前,SUA命题组的老前辈 @Dai@NeverLand 曾经写过这样一篇文章:

今年,XCPC系列的命题算是揭开了新篇章,恰逢SUA命题组(含前身)创建第七年,作为本次区域赛的主要负责人之一,我想借这个机会和大家分享一下我们SUA的命题周期,以及我们对于区域赛命题的心得。嘤其鸣矣,求其友声,也欢迎大家多多提意见,帮助我们变得更好。

比赛总体情况

最后2个0题队伍,34个1题队伍,186个2题队伍,166个3题队伍,119个4题队伍,95个5题队伍,46个6题队伍,18个7题队伍,6个8题队伍,3个9题队伍,4个10题队伍,2个11题队伍,1个12题队伍(含非正式参赛),无队伍通过所有题目。

A题通过680队,M题通过645队,C题通过454队,H题通过290队,D题通过180队,J题通过69队,I题通过32队,E题通过29队,G题通过15队,L题通过6队,B题通过3队,K题通过2队,F题通过1队。所有题目均有队伍通过。

按照35,70,105来划分,金牌线是7题以上及3个6题队伍,银牌线是5题594罚时,铜牌线是4题480罚时。具体以主办方颁奖名单为准。

冠军是北京大学_逆十字12题,亚军是清华大学_小万邦11题,季军是香港中文大学(深圳)_新手上路10题。

含打星队伍,恰好有10支队伍通过9题及以上。

基本符合赛前对各分数线的预测。

命题过程及心得

8月20日,我们接受了南京站命题申请,此时定下的比赛日期是11月6日,线下举行。同日,我们建立了南京出题分群,建立了pool,用来收集大家题目idea。

9月30日,国庆假期前,我从pool中选择了11个题,要求大家在国庆假期内造完。

国庆假期时得知南京站延期至12月4日,于是出题期限延期至11月第一个星期,有13道题目在计划中。

11月14日进行了第一场验题赛,邀请了三位wf级别的退役选手进行验题(定位为一支稳金队),最后题数8,值得一提的是,在验题赛前3天,我认为题目过难,将字符串hard题exclude了,同时担心以构造题作为签到题不合适,补充了一道a+b。我们将这个版本称为南京1.0。

赛后和验题队伍进行了交流,将那道a+b级别的题目(认为有点不尊重选手),一道金牌题(题目类型重复)和一道压轴级别的几何题目移除。补充了两道银铜牌题,及将一道简单的找规律/贪心题(最后比赛的M题)重新选入比赛。我们将这个版本称为南京2.0。

11月21日进行了第二轮验题赛,邀请了由之后某XCPC比赛出题组组成的两支水平分别为出线及稳金队进行验题,最后题数为10和8。

11月22日,邀请了Georgia Tech 1和MIT_CopyPaste(感谢邓老师,zzq还有anton)进行验题,定位为金牌队和夺冠队。Georgia Tech 1做了7题,邓老师仅用了3小时33分钟就AK了题目。

11月23日,邀请了国内两支出线级别的队伍进行验题,题数最后为10和8,我们由此预计出线题数9,捧杯题数10,夺冠12-13。

11月28日,邀请Um_nik和他的队伍进行验题,在验题过程中,认为新加的铜牌题与C题难度过于相似,再考虑到南京参赛队伍强度,移除后加回了早前的压轴几何题,Um_nik最后题数12,K题没写完。我们由此预计夺冠队伍应该不会AK。该版本称为南京3.0。

11月29日,考虑到原有的I题和压轴几何题有内容重合,思考再三后将该题改为现在的I题,最后邀请了今年的某位ICPC/CCPC双料命题人进行验题。该版本称为南京4.0,也就是最后的版本。

12月1日,题目成功搬运到PTA上,我将所有验题代码提交了一次,设定了最后的时限,该过程耗时2小时。

12月3日,热身赛。

12月4日,正赛。

由于担心错题或者数据强度不够的情况,我们每个题平均有3份以上正确代码,10份各式错误的代码,共有9队次的验题,codeforces上相关提交有8页。我们总共准备了19道题目,题面经过了多次修缮,力求题面风格统一,但不要影响队伍阅读,在此也感谢邓老师,zzq还有um_nik关于题面提出的意见。最后我们预测是夺冠12题,捧杯/出线10题,金牌7题,银牌5题,铜牌4题。

我们SUA的命题方针,是力求尊重每一位参赛选手,无论是前排队伍,还是银铜区,乃至第一次参加相关比赛的新人。我们希望我们的题目能够清新自然,不要有算法门槛,不要成为算法大战,希望在考察队伍算法设计水平的同时,也考察队伍比赛经验,比赛策略,希望不同特点的队伍都能找到适合自己的题目,希望不同水平的队伍全程都能有题目进行尝试。一言以蔽之,以人为本,就是我们的宗旨

更具体地,我们希望我们每一个题目都能有队伍尝试和通过。对于简单题,我们从不把其看作”施舍“,而是努力去揣摩后排队伍水平,希望能够打造出适合他们的题目。对于难题,我们希望前排队伍也能在做完后有所进益。我们希望所有队伍在做完我们题目后能够有所收获,希望能对大家未来的比赛有所帮助。

想法很多很理想,但现实可能很骨感。我们也不敢妄自尊大,觉得我们现在做得有多好。路漫漫其修远兮,吾将上下而求索。我们一定会继续努力,不忘初心,努力为大家打造高质量的比赛题目。

题解


A. Oops, It’s Yesterday Twice More

注意到我们可以通过 n-1 次左(右)移,再加上 n-1 次上(下)移将所有袋鼠移到一个角落,然后正常沿曼哈顿距离走到目标点即可。选择最靠近目标点的角落作为集结点,则可在限制步数内完成。

B. Puzzle in Inazuma

通过对两个图的每一条对应的边的权值做差,可以把题意转换为给定一个图 G ,询问是否可以通过有限次操作把所有边权都变成 0 .

我们先来讨论 n\geq 6 时的情况。

首先不难发现,每次操作后总和必须一样。接下来我们说明只要总和一样的时候,图 G 一定可以变成图 H .

我们假设图中存在 6 个点 A, B, C, D, E, F ,我们进行如下一系列操作:

- 将 DA, DB, DE 边上的值 -1 ,将 AB, BE, EA 边上的值 +1 .

- 将 DA, DB, DC 边上的值 +1 ,将 AB, BC, CA 边上的值 -1 .

- 将 EA, EC, ED 边上的值 +1 ,将 AC, CD, DA 边上的值 -1 .

- 将 EA, EB, EC 边上的值 -1 ,将 AB, BC, CA 边上的值 +1 .

经过以上操作后,我们将 AB, AE 边上的值增大了 1 ,将 AC, AD 边上的值减小了 1

类似地,我们可以将 AB, AE 边上的值减小 1 ,将 AC, AF 边上的值增大 1

从而我们可以通过有限次操作将 AD 边上的值减小了 1 ,并将 AF 边上的值增大了 1

因此 n\geq 6 的时候只要总和一样,则一定可以在有限步之内将图 G 变成图 H

接下来讨论 n=5 时的情况,

通过 4 次操作,我们可以将 AB, AE 边上的值增大了 1 ,将AC, AD 边上的值减小了 1

类似地,我们可以将 AB, AC 边上的值增大了 1 ,将 AD, AE 边上的值减小了 1

从而我们可以通过有限次操作将 AD 边上的值减小了 2 ,并将 AB 边上的值增大了 2

因此我们只需考虑奇偶性,可以通过 O(2^{\binom{n}{2}}) 的复杂度完成。(其实可以优化成 O(2^n)

最后讨论 n=4 时的情况,相当于 4 个未知数, 6 个方程,直接找有没有解即可。

C. Klee in Solitary Confinement

我们枚举最后众数为 x ,则每次只需要单独考虑 xx+k 。我们事先可以将每个数按数值大小,将位置插入vector,则可做到均摊 O(n) 。如果使用map或者别的容器实现,则有运行超时的风险。

现在问题转化成有一个长度为 m 的序列,序列仅由 XY 组成,用 X_{l,r}Y_{l,r} 表示区间 [l,r]XY 的个数,则我们需要选择一个区间 [l,r] ,使得 X_{1,l-1}+Y_{l,r}+X_{r+1..m} 最大。

简单转化一下,则对于每一个 r ,我们需要最大化 X_{1,l-1}+Y_{l,r}+X_{r+1,m}=X_{1,l-1}+(r-l+1)-X_{l,r}+X_{r+1,m}=2 \times X_{1,l-1}+(r-l+1)-X_{1,r}+X_{r+1,m} 。整理得到 (2*X_{1,l-1}-l)+(r+1-X_{1,r}+X_{r+1,m}) ,即最大化 2*X_{1,l-1}-l ,记录前缀最大值转移即可,时间复杂度 O(m)

综上,时间复杂度为 O(n)

D. Paimon Sorting

先考虑序列所有元素各不相同的情况。

首先从第一个元素开始,找到并跳到当前元素右边第一个比它大的元素。称这些元素为``特殊元素''。可以发现外层循环的第一轮会把所有特殊元素``右移''一位,此时序列中最大的元素就到了序列第一个。

从外层第二轮循环开始,对于第 i 轮循环,序列前 (i - 1) 个元素已经是有序的。设此时第一个比 a_i 大的元素是 a_k ,那么第 i 轮循环将会发生 (i - k) 次交换。因此,非特殊元素将会贡献``前面有几个数比它大''次交换(注意一开始特殊元素的右移对答案没有影响,因为虽然移走了一个比它大的元素,但是最大的元素移到了开头),而特殊元素将会贡献 2 次交换。

接下来考虑存在相同元素的情况。

对于非特殊元素,前面比它大的,但是相同的元素只会发生一次交换,因此需要对元素去重一下再统计。另外,如果这个非特殊元素恰好等于上一个特殊元素,那么上一个特殊元素的右移会导致该非特殊元素的贡献增加 1

实际解题过程中,由于暴力程序非常简单,可以考虑通过对拍发现并调整 corner case。

E. Paimon Segment Tree

我们可以通过差分询问时间,将单个询问 [x,y] 转化为两次询问 [0,y][0,x-1] 的结果之差。再通过离线询问,我们不需要实际记录每个时间节点的信息,空间上就不容易超限了。同样,为了对齐时间,我们将 \{[l,r],k\} 视作3次操作 \{[1,l-1],0\},\{[l,r],k\},\{[r+1,n],0\}


我们考虑使用线段树及懒标记直接维护当前时间的区间信息,那么我们只需要考虑标记下放和区间合并。


对于每个节点 [l,r] ,我们维护以下信息:

x_1=r-l+1 ,区间长度;

x_2=\sum_{i=l}^r a_i^{t} ,线性和;

x_3=\sum_{i=l}^r {a_i^{t}}^2 ,平方和;

x_4=\sum_{j=0}^{t} \sum_{i=l}^r {a_i^{j}}^2 ,历史平方和;



对于每次增量 k ,我们有以下的转移关系:

x_1=x_1 ;

x_2=x_2 + x_1 \times k ;

x_3=x_3 + 2 \times k \times x_2 + k^2 \times x_1 ;

x_4=x_4 + x_3 + 2 \times k \times x_2 + k^2 \times x_1 ;



我们如果考虑一个向量 \{x_1,x_2,x_3,x_4\} ,则可写出以下转移矩阵:


\begin{equation} \left( \begin{array}{cccc} 1 & k & k^2 & k^2\\ 0 & 1 & 2k & 2k \\ 0 & 0 & 1 & 1 \\ 0 & 0 & 0 & 1 \\ \end{array} \right) \end{equation}


因此,我们只需要将转移矩阵作 4 \times 4 为标记下放,即可用线段树维护当前信息,这样复杂度是 q \log n 的。我们注意到矩阵大小为,则每次矩阵乘法运算次数为 4^3 ,常数比较大。经过分析,我们会发现有效转移只有从 x_ix_j(j>i) ,则实际只需要求出 C(4,2)=6 个系数即可,常数大幅减少。在题目的时限设置中,我们设置了一个让不需要任何矩乘优化就可勉强通过的时限。

F. Paimon Polygon

本题总共分为两种情况: AB 包含或相离。


对于包含的情况,需要满足所有点都在某条直线的一侧。我们可以先对所有点构建一个严格凸包 A ,再利用剩下的点构建一个严格凸包 B ,同时要求不存在 B 内侧的点。注意需要特判共线的情况,即如果存在三点共线 x,y,zA 中会自动选取两侧的点 x,zB 中则选取了剩下的点 y ,但其实这种情况是不合法的(有除了 O 以外的新的交点了)。当然我们也不能简单地认为三点共线一定不合法,如果 y 外侧还有一个点 y' ,那么 A 选择 x,y',zB 选择 y 是可能合法的。


对于相离的情况,我们首先可以尝试对所有点构建一个凸包。如果构建失败,违反的三个点处必须划成两个集合,然后以此为起点左右扫一遍即可;如果构建成功,我们可以做一个类似旋转卡壳的操作,把 360 °的点划成两段连续的集合,一段给 A 一段给 B ,同时要求每段的夹角不能大于等于 180 °。

G. Paimon’s Tree

考虑一个弱化的问题:假设在树上指定一条路径,求这条路径的最大长度。

树上除了在指定路径上的边,还有一些不在指定路径上的边可以让我们放``垃圾''。例如我们要求样例 1 中路径 1 \to 3 \to 4 的最大长度,节点 3 就有 1 条边可以放垃圾,节点 42 条边可以放垃圾。如果指定的是路径 1 \to 3 \to 2 ,那么节点 3 就有 3 条边可以放垃圾,而节点 12 都没有边可以放垃圾。


设指定的路径有 k 个节点,其中第 i 个节点可以放 b_i 个垃圾,我们就可以进行区间 dp。记 f(l, r, t) 表示已经完成了路径中第 l 到第 r 个节点之间的边的赋值,而且已经从序列 A 中用掉了 t 个数(也就是说树里已经放了 (t - (r - l)) 个垃圾)。有如下转移方程:

f(l, r, t) \to \begin{cases} f(l - 1, r, t + 1) + a_{t + 1} & \text{if }l > 1 \\ f(l, r + 1, t + 1) + a_{t + 1} & \text{if }r < k \\ f(l, r, t + 1) & \text{if }(t + 1) - (r - l) \le \sum\limits_{i=l}^r b_i \end{cases}


分别是往左或右拓展一条边,或再塞一个垃圾到目前的节点里(要能塞得下才行),答案就是 f(1, k, n) ,复杂度 \mathcal{O}(n^3)


接下来考虑原问题,也就是把这个 dp 过程拓展到树上。此时会发现无法确定路径的两个端点能放多少垃圾,因为端点可以向它的任意邻居扩展。因此我们把``端点接下来要向哪里扩展''加入 dp 状态。dp 状态变成了 f(u, v, t, mask)mask \in [0, 3]

f(u, v, t, 0) 表示路径 u \leadsto v 的最大长度,而且路径不再向两端扩展。

f(u, v, t, 1) 表示路径 u \leadsto p_v 的最大长度( p_v 是路径 u \leadsto v v 的前一个节点),而且端点 u 不再扩展,但端点 p_v 接下来会向 v 扩展(也就是说, v 以及 v 的子树里都不能放垃圾)。

f(u, v, t, 2) 表示路径 p_u \leadsto v 的最大长度,而且端点 p_u 接下来会向 u 扩展,但端点 v 不再扩展。

f(u, v, t, 3) 表示路径 p_u \leadsto v 的最大长度,而且端点 p_u 接下来会向 u 扩展,端点 p_v 接下来会向 v 扩展。


转移与之前类似,答案就是 \max f(u, v, n, 0) ,复杂度 \mathcal{O}(n^3)

H. Crystalfly

当我们初次到达某个节点 u 时,它的所有儿子会被激活,由于 t_i \le 3 ,所以我们的决策只有两种,一种是进入某个儿子 v 后继续向下,这样 u 的所有其它儿子的蝴蝶都无法被获取。另一种是进入某个儿子 v 获取 a_v 后立即回到 u ,然后进入另一个儿子 w 并获得 a_w ,这要求 t_w = 3 。我们令 f_u 表示节点 u 在我们进入之前所有蝴蝶都已经消失,同时它的孩子在我们进入 u 之前都未被激活,这棵子树的最优答案,那我们要求的最终答案就是 f_1 + a_1 。我们令 ch_{u} 表示 u 的儿子集合, sum_{u} 表示 u 的所有儿子 vf 值之和。考虑两种决策对应的转移,第一种决策:

t_u = \max_{v \in ch_{u}}{sum_{u} + a_{v}}

第二种决策:

t_u = \max_{v, w \in ch_{u}, v \not= w, t_{w}=3}{sum_{u} - f_{v} + a_{v} + sum_{v} + a_{w}}


第二种决策需要在转移时枚举 v ,并且找到符合条件的 wa_w 最大的值,略微维护一下即可。总复杂度 O(n) .

I. Cloud Retainer’s Game

假设不存在挡板,那么小球的移动路线中,向右下移动的部分满足 (x + y) \bmod 2H = k ,向右上移动的部分满足 (2H - y + x) \bmod 2H = k

f(k) 表示特征值为 k 的线路的最优答案。碰到金币 (x, y) 时, f((x + y) \bmod 2H)f((2H - y + x) \bmod 2H) 均增加 1 ;碰到挡板 (x, y) 时,由于可以移除挡板, f((x + y) \bmod 2H)f((2H - y + x) \bmod 2H) 均取二者中的最大值。

这真的是一个期望银铜难度的题目!!!!!!!!!!!

J. Xingqiu’s Joke

如果只使用前两种操作,那么 \delta = a - b 的值是不变的,而且 ab 的公共质因数一定也是 \delta 的质因数。而且当我们想除以一个质因数 g 时,我们一定会先通过前两种操作来到最近的 g|ag|b 的状态然后直接除 g (先加减 k ,再除 g ,再加减 1 肯定优于加减 2k )。


因此记 f(a, \delta) 表示从 (a, a + \delta) 得到 1 的步数,枚举 \delta 的质因数 gf(a, \delta) 可以由 f(\lfloor\frac{a}{g}\rfloor, \frac{\delta}{g})f(\lceil\frac{a}{g}\rceil, \frac{\delta}{g}) 转移得到。因此状态的第二维只有 \delta 的因数个。


但状态的第一维如果和除以因数的顺序有关,那状态数又超了。但无需担心,实际上对于整数 xa_1, a_2, \cdots, a_kx 以任意顺序被 a_i 进行整数除法(无论上取整或下取整),结果至多只有两种。因此总的状态数仍然是因数级别的。

通过以下方式完成证明:

x 以任意顺序被 a_i 进行下取整的整除得到的结果是 k_1 ,进行上取整的整除得到的结果是 k_2 ,那么

k_1\prod\limits_{i=1}^n a_i \le x \le (k_1 + 1)\prod\limits_{i=1}^n a_i - 1

(k_2 - 1)\prod\limits_{i=1}^n a_i + 1 \le x \le k_2\prod\limits_{i=1}^n a_i

该式可通过数学归纳法证明。

上面两个式子可以看作长度为 (\prod\limits_{i=1}^n a_i - 1) 的两个区间,显然只有当 k_2 + 1 = k_1k_2 = k_1 时两个区间才有交集。

由于以任意顺序进行全部上取整的整除得到的结果一定是最大的,进行全部下取整的整除得到的结果一定是最小的,那么上下整除混合得到的结果自然在两者中间。该性质得证。

K. Ancient Magic Circle in Teyvat

考虑容斥原理,记 U 为所有的四点组,由于四点组里两两连了六条边,记 A_i 表示第 i 条边是红色的四点组,那么有

\left| \bigcap_{i=1}^{6} \overline{A_i} \right| = \left| U \right| + \sum_{k=1}^{6} (-1)^k \sum_{1 \le i_1 < i_2 < \ldots < i_k \le 6} \left| A_{i_1} \cap A_{i_2} \cap \cdots \cap A_{i_k} \right|


于是有

\left| \bigcap_{i=1}^{6} \overline{A_i} \right| - \left| \bigcap_{i=1}^{6} A_i \right| = \left| U \right| + \sum_{k=1}^{5} (-1)^k \sum_{1 \le i_1 < i_2 < \ldots < i_k \le 6} \left| A_{i_1} \cap A_{i_2} \cap \cdots \cap A_{i_k} \right| \tag{$*$}



\left| \left| \bigcap_{i=1}^{6} \overline{A_i} \right| - \left| \bigcap_{i=1}^{6} A_i \right| \right| 即为所求,那么需要计算出 (*) 式右边的每一项,


首先列出所有本质不同的 11 种四点组之间连边情况,如果将红色边看成是有限制(对应边一定是红色),蓝色边看成是没有限制(对应边可以是红色也可以是蓝色),除去最后一种全是红色边的情况,其他 10 情况都可以在 O(m\sqrt{m}) 的复杂度,也就是枚举三元环和计数四元环的复杂度下计算出来,从而得到 (*) 式右边每一项的值,也就得到答案了。

L. Secret of Tianqiu Valley

可以找规律把方程解出来,得到那些位置需要点奇数次那些位置偶数次。每次如果场上有一个奇数次的位置可以点,贪心点掉,这样是一步解决一个位置。如果场上没有:

场上有至少一个目前灭掉的灯,它一定需要被影响(直接+间接)奇数次;但同时它本身需要被点偶数次,有

目前情况:0
方程奇偶:0

在它左侧或右侧有一个需要被点的位置,但这个位置当前已经是亮着的了。

假设这个被点的位置在右侧,有

目前情况:01
方程奇偶:01

这个已经被点亮的位置需要被影响偶数次,因此再右侧一个位置一定是需要被点的;可以推得它一定也已经被点亮了,有

目前情况:011
方程奇偶:011

现在我们可以依次点第 1,2,1,3 个位置,四步解决两个两个位置,因此最多 2n

M. Windblume Festival

每个数对答案贡献只能为 1-1 ,显然至少要有一个 1n>1 时至少要有 -1

对于任意一个符合上述条件的贡献方案,选一个 1 作为起点,之后的每个 1 可以和在其之前最近的 -1 合并,之后所有 -1 可以和起点 1 合并,因此所有符合条件的方案均合法。

答案是 max-min+sum(rest)

编辑于 2021-12-05 07:21