【题解】2025 UESTCPC 初赛

据说题比去年初赛要简单,但我去年抱上了 lyc 大腿,所以完全不记得去年难度怎么样了。

今年来验题,但水平有限(集训队垫底水平),只验了一些简单题。

其中 A 题是经出题人提醒做出来的,D 偷看了 Tag,H 和 L 则完全不会,等有出题人题解了我再补上来。代码都写的很唐,也不一定是最优解,不要笑话我QAQ。


主观难度分布(H、L 未做出):

  • 签到:F,G,J,N。
  • Easy:B,C,I,O,P。
  • Easy_Mid:K,M。
  • Mid:D,E,Q。
  • Mid_Hard:\(\varnothing\)
  • Hard:A。

写下这段话的时候还有三天才初赛,大胆猜测决赛线是 \(4\) 题(签到即送)。

(upd on 04.01)大一队伍 4 题,非大一队伍 5 题,外校未知。


(upd on 赛后)按榜来看 H 是 Easy/Easy_Mid,L 是 Mid_Hard/Hard。


(upd on 03.31)来点链接。

补题链接:Cdoj

题面:Statement

官方题解:Solution

查重前榜单:Scoreboard


(upd on 赛后)来点 Statistics。

本场比赛共有 405 支队伍报名,其中正式队 278 支,打星队 127 支。

有效参赛队伍(至少通过 \(1\) 题)总计 309 支,其中正式队 212 支,打星队 97 支。

最终 3 支队伍 AK,过题数情况如下。

组别 | 过题数 17 16 15 14 13 12 11 10
正式队 1 0 0 0 1 0 2 4
打星队 2 0 1 2 2 8 8 6
组别 | 过题数 9 8 7 6 5 4 3 2 1
正式队 9 14 8 14 23 14 56 35 31
打星队 6 3 8 10 9 5 17 6 4

接收到 8978 份有效 submissions,1698 份答案正确。


(upd on 初赛日)来点赛时趣事:

08:58 哎我草怎么是我发题面,赶紧登上 dj 下了个发到群里。

09:20 卧槽了,C 怎么被暴力过了,还碾压 std。验题的时候不交暴力导致的。

09:43 大哥给 C 加了一组数据,好很多了。

10:03 哎哎,I 的精度被浮点数硬抗过去了,精度大师。

10:47 Queue 莫名其妙 5min 了,有点爆了。

10:55 Queue 越来越长了,爆爆爆,准备多搞几台服务器加 judgehost。

11:20 加了一台 8c16g 开了 8 个 judgehost,把原来的关掉了,只跑 web。

11:27 貌似有点好转(?)

11:31 好起来了。终于有时间看榜,发现 M 竟然还没人过啊,低估了。

Fun Fact:在 pool 里 M 的难度是 *1300,有点幽默。

11:41 DeepSleep 这么厉害啊,队里两个 2000+ 就是不一样。

11:52 网页 502 了????

11:55 k4c 很快就发现 /etc/php/8.2/fpm/pool.d/domjudge.confrequest_slowlog_timeout 的值写成了 10si,多了个 i。改完马上又好了。

12:01 L 被开出来了啊。你知道的,我一直是 DeepSleep 的粉丝。

下午就比较清闲了,大概就是时不时把 judgehost 的 internal error 给 resolved 一下。

本来想润去打洛谷的蓝桥杯模拟赛,斟酌了一下还是算了。

这期间 QQ 收到了若干人机大学生发来的弱智让人啼笑皆非的私信。

15:37 sooke 拿到了 first AK!

16:58 第一发交 clar 的代码出现了。

不知道会不会有往 clar 里交奶龙的,很期待啊!

19:08 DeepSleep 也 AK 了,仿佛看到了泥电的下一支 wf 队。

20:10 爆爆爆,O 被乱搞搞过去了。更有戏剧性的是我看出题人的题解,突然发现我验题的时候写了个错解(下面改正了),太难绷了。

在这里给大伙磕头了。


A. 炼金术士

拉姆齐定理指出,\(6\) 个点的完全图(\(K_6\))用两种颜色任意着色(每条边都必须着色),必然存在至少一个单色三角形。

这意味着我们选出的 "黑化边" 与 "白化边" 并集的边导出子图一定不含 \(K_6\)

发扬人类智慧,考虑这么一种构造:将 \(n\) 个点划分成尽可能平均的 \(5\) 个集合,每个集合内部互不连边,不同集合间全部连边。

根据抽屉原理,从中任意选出 \(6\) 个点,至少有 \(2\) 个来自同一集合,没有连边,故不含 \(K_6\)。在此基础上,容易说明这样的构造具有最大连边数。

接着,我们要将这些边划分成两个集合,每个集合不含三元环,且其中一个集合能塞得下给定的长度为 \(k\) 的无环链。

再度发扬人类智慧,给上述 \(5\) 个点集编号 \(0\sim 4\),进行如下构造:

  • "黑化边":点集 \(i\) 中的所有点向点集 \((i+1)\bmod 5\) 中的所有点连出的边。
  • "白化边":剩下的所有边。

因为一个三元环肯定有一条边的两个端点不来自相邻的点集,故 "黑化边" 不含三元环。

而 "白化边" 要满足 "任意一条边都不来自相邻点集",则至少需要 \(6\) 个点集,不满足。

此外,在这 \(5\) 个点集上绕圈圈,一定能构造出一条包含所有 \(n\) 个顶点的链,将给定的链随便插进一个位置即可。

时间复杂度 \(O(n^2)\)

上图是 \(n=7\) 的图例,\(5\) 个集合的大小分别为 \(2, 2, 1, 1, 1\)


B. 简单可满足性问题

假定已经将所有变量赋过了值,可以分出以下三类子句。

  1. 该子句所有变量均为 \(1\),此时子句的值为 \(1\)
  2. 该子句所有变量均为 \(0\),此时子句的值为 \(0\)
  3. 该子句部分变量为 \(1\),此时子句的值为 \(1\)

注意到当我们给所有变量取反时,第一和第二类子句个数互换,第三类子句个数不变。

而 "第一 + 第三" 类子句的个数和 "第二 + 第三" 类子句的个数总有一个 \(\ge \lceil\frac{m}{2}\rceil\)

给所有变量任意赋值,验证不满足后就全部取反,总有一个满足条件。

时间复杂度 \(O(m)\)


C. 箭串

将询问离线。暴力做就是倒序遍历每个区间,如果是 *,就覆盖;否则不操作。

std::set 维护当前还存活的 * 的位置。对于一个询问 \([l,r]\),二分出 std::set 中第一个 \(\ge l\) 的位置,迭代器一直向右,修改沿途位置即可。

时间复杂度 \(O((n+m)\log n)\)


D. 阿罗祖斯坦的桥

\(f[l][r],g[l][r]\) 分别表示在 \([l,r]\) 里建桥的最大数量和最小总长度,然后就是一个裸的区间 dp。

发现 \(w(l,r)=d\cdot \text{arcsin}(\frac{\text{dis}(l,r)}{d})\) 具有单调性(跨度大桥更长)和四边形不等式条件(交叉弱于包含),于是套一个四边形不等式优化的板子就过了。

时间复杂度 \(O(n^2)\)


E. 猫猫城大选(困难版)

把问题 format 一下:给定一个变量 \(x\),初始为 \(0\),四种操作如下,问最终 \(x\) 的期望。

  1. \(x\leftarrow x+1\)
  2. \(x\leftarrow x-1\)
  3. \(\begin{cases}x\leftarrow x+1 &,x>0\\ x\leftarrow x-1&,x<0\\ p_i \text{ 概率}+1,(1-p_i)\text{ 概率}-1 &,x=0\end{cases}\)
  4. \(\begin{cases}x\leftarrow x-1 &,x>0\\ x\leftarrow x+1&,x<0\\ p_i \text{ 概率}+1,(1-p_i)\text{ 概率}-1 &,x=0\end{cases}\)

我们发现对于两个 \(x=0\) 的位置,其间进行的操作是确定的。

具体的,假设当前时刻操作前 \(x=0\) 且当前为第 \(3/4\) 类操作:若选择了 \(+1\),则直到下一次 \(x=0\) 之前 \(x\) 的值都是正的;若选择了 \(-1\),则直到下一次 \(x=0\) 之前 \(x\) 的值都是负的。

\(dp[i]\) 代表若第 \(i\) 个时刻操作前 \(x=0\),最后 \(x\) 的期望值。

从后向前转移,\(dp[i]=p[i]\cdot f(i)+(1-p[i])\cdot g(i)\)

其中 \(f(i)/g(i)\) 代表第 \(i\) 时刻选择了 \(+1/-1\) 的情况下,之后的贡献。

\(f(i)\) 为例,若之后不存在 \(x=0\) 的时刻,\(f(i)=a_n-a_i+1\),其中 \(a_i\) 表示假定 \(x\) 一直 \(>0\),按顺序执行完第 \(i\) 次操作后的结果;否则设这 \(i\) 之后第一个 \(x=0\) 的时刻为 \(\text{nxt}\),有 \(f(i)=dp[\text{nxt+1}]\)

\(\text{nxt}\) 的过程可以用线段树实现,时间复杂度 \(O(n\log n)\)


F. 我知道你姓啥!

竖着看,相当于 \(m\) 个长为 \(n\) 的字符串。我们的目标是使这 \(m\) 个字符串互不相同。

一次添加操作相当于给每个字符串末尾添上 \(0\)\(1\)。对于两个相同的字符串,在其末尾一个添 \(0\),一个添 \(1\),就能让它们不同。

std::map 统计出原来的 \(m\) 个字符串中出现次数最多的串,设其出现次数为 \(c\)。一次操作能让 \(c\leftarrow \lceil\frac{c}{2}\rceil\),暴力模拟即可。

时间复杂度 \(O(nm\log m)\)

Fun Fact:赛前出题人打算把 std::unordered_map 卡掉,但因为是签到,所以后来觉得没有必要就没卡。

Bonus:可以用 Trie 做到 \(O(nm)\)

Fun Fact2:你可能会因为看到我 F 和 P 的题解和官方题解一模一样,然后联想到我是出题人。这是错的,出题人只是不想写题解了就让我发一份上去。


G. 猜数游戏

可以找到 \(\ge l\) 的最小的满足 \(x\bmod p=a\) 的位置 \(i\),判断 \(i\) 是否 \(\le r\)

\(i=kp+a\ge l\),得 \(k\ge \lceil\frac{l-a}{p} \rceil\),故 \(i=\lceil\frac{l-a}{p}\rceil p+a\)

时间复杂度 \(O(1)\)


H. 独立事件

(upd on 03.31)

\(P(A)=0/1\) 时,答案是 trivial 的。否则,存在以下推导: \[ \begin{align} &P(A)P(B_I)=P(A\cap B_I)\\ \rightarrow\ &P(B_I\mid A)=P(B_I\mid \overline{A}) \\ \rightarrow\ &\frac{P(AB_I)}{P(A)}=\frac{P(\overline{A}B_I)}{P(\overline{A})} \end{align} \]

\(f[i]\) 表示在事件 \(A\) 对应的集合中选出元素总和为 \(i\) 的方案数。

\(g[i]\) 表示在事件 \(\overline{A}\) 对应的集合中选出元素总和为 \(i\) 的方案数。

那么答案是:

\[ \sum_{i=0}^{1000}\sum_{j=0}^{1000}f[i]\cdot g[j]\cdot [\frac{i}{P(A)}=\frac{j}{P(\overline{A})}] \]

其中,\(P(A)=\dfrac{\sum\limits_{i=1}^{k}a_i}{1000}\)\(P(\overline{A})=1-P(A)\)

\(f[i]\)\(g[i]\) 可以分别用 \(01\) 背包求出。

时间复杂度 \(O(nw+w^2)\)\(w\) 为值域。


I. 圆与直线交点

因为不能出现浮点数,所以整个计算过程要保留分数形式。

python 的 fraction 是简单的,能帮你自动约分,四则运算也很方便。C++ 要么手写分数类,要么另谋出路。

  1. 使用外心坐标公式求出三角形的外心坐标 \((X,Y)\)
  2. 任取三点之一,坐标公式计算其与外心的距离,得到半径的平方 \(R^2\)
  3. 由点到直线的距离公式,有:

\[ d=\frac{|(X-P_x)V_y-(Y-P_y)V_x|}{\sqrt{V_x^2+V_y^2}} \]

  1. 移项,两边平方,得到相切的充要条件 \([(X-P_x)V_y-(Y-P_y)V_x]^2=R^2(V_x^2+V_y^2)\)
  2. 相交和相离改等号为不等号即可。

时间复杂度 \(O(1)\)


J. 创建用户

按照题意模拟即可。

C++ 使用 getline 读入一整行。


K. 哲学之门

想好怎么枚举是重要的。

我的做法是枚举三度点 \(i\),遍历 \(i\) 的邻接点,让每个邻居都尝试作为 \(O\),这样 \(A,B\) 就是另外两个邻居。

再枚举 \(A\) 的邻居 \(C\)\(B\) 的邻居 \(D\)。将 \(C\) 的邻居放进一个容器里,然后枚举 \(D\) 的邻居,若和 \(C\) 共有就是 \(x\)

时间复杂度 \(O(n)\)


M. 前兜车一转后兜车一转

选定的前缀和后缀可以是不交、恰交、相交,每种情况都讨论一下。

恰交是简单的。

不交的话预处理出 \(suf[i]\) 代表翻转 \(i\sim n\) 中的任一后缀的最大收益。接着枚举前缀,另一段的最大贡献就能查表了。

相交的话是类似的。枚举 \(i\) 代表翻转前缀 \(1\sim i\),此时要选定一个 \(j\in [1,i]\) 然后翻转后缀 \(j\sim n\) 并让贡献最大。推一下式子,这等价于找到让 \(|a_j-a_n|-|a_j-a_{j-1}|\) 最大的 \(j\),一样可以预处理出一个前缀和。

时间复杂度 \(O(n)\)


N. 幸运之环 II

用 dfs 找出这棵基环树的环。

找到环上最小的点 \(x\),再看一下 \(x\) 沿环上两个方向的邻居,哪个邻居小就往哪个方向输出。

时间复杂度 \(O(n)\)


O. 不走回头路

在一个强连通分量内,所有的点都可以互相到达。即同一个 SCC 内的景点可以任意安排游览顺序。

缩点,得到一个 DAG。若要能遍历所有的点,这个 DAG 必然是一条链。

在 DAG 上 dp(令 \(f[x]\) 代表以 \(x\) 结尾的链的长度的最大值),判断是否存在一个点 \(f[x]=cnt\) 即可(\(cnt\) 是缩点后的图的顶点数)。

时间复杂度 \(O(n)\)

FunFact:一个误区是只判断缩点后的图 "恰有一个入度为 \(0\) 的点" 和 "恰有一个出度为 \(0\)" 的点,这是错误的,反例是 \(1\to 2\to 3,1\to 4\to3\)。赛时的数据有这个缺陷(包括其它一些没缩点乱搞度数的做法也过了),磕头 \(+1\)。补题链接里的数据是更新过的。


P. 相同字符

预处理 \(pre[i]\) 代表从前向后在 \(a\) 中匹配上 \(b\) 中的字符的个数,即 \(a[0..i]\) 能匹配上 \(b[0..(pre[i]-1)]\)

预处理 \(suf[i]\) 代表从后向前在 \(a\) 中匹配上 \(b\) 中的字符的个数,即 \(a[i..n-1]\) 能匹配上 \(b[(m-suf[i])..m-1]\)

上述的匹配不区分大小写。

\(a\) 中无大写字母,判断 \(\max(suf[0],pre[n-1])=m\) 是否成立即可。

否则枚举 \(b\) 中每一个与大写字母相同的位置 \(i\),判断 \(pre[i-1] \ge i\)\(suf[i+1]\ge m-i-1\) 是否成立。

时间复杂度 \(O(n)\)


Q. 校车

数据范围识状压。将给定的 \(w\) 个路口称作关键点。

\(dp[S][i]\) 代表从起点出发经过了状态 \(S\) 中的点,最终到达关键点 \(i\) 所需的路程。

  • 初态:\(dp[\{i\}][i]=\text{dis}(1,p_i)\)
  • 转移:\(dp[S\cup \{j\}][j]=\min(dp[S\cup \{j\}][j],dp[S][i]+\text{dis}(p_i,p_j))\),其中 \(i\in S,j\not\in S\)

那么经过 \(S\) 中的点再返回起点的最短距离 \(f[S]=\min\limits_{i\in S}(dp[S][i]+\text{dis}(1,p_i))\)

二分答案,设二分出的答案是 \(mid\),问题转化为判断是否能用不超过 \(k\)\(f[S]\le mid\) 的路径覆盖所有 \(w\) 个关键点。

\(g[S]\) 表示覆盖 \(S\) 中的点需要的最小路径条数,判定就是 \(g[\{1,2,\ldots,w\}]\le k\)

  • 初态:\(g[\varnothing]=0\)
  • 转移:\(g[S\cup T]=\min(g[S\cup T],g[S]+1)\),其中 \(f[T]\le mid\)

注意:转移的时候不需要对每一个 \(S\) 都枚举所有的 \(T\),那样是 \(O(4^w)\)。只需要 \(O(3^w)\) 枚举所有所有状态的子状态就行。

时间复杂度 \(O(wm\log m+w^22^w+20\cdot 3^{w})\)


【题解】2025 UESTCPC 初赛
https://kisuraop.github.io/posts/94193aa7.html
作者
KisuraOP
发布于
2025年3月29日
许可协议