Codeforces Round #738 (Div. 2) 解题报告

这里是 Codeforces Round #738 (Div. 2) 的解题报告 qwq,所有代码都已提交至 github 仓库(可能随着水平的增长,这种将一个比赛所有题目都写一遍题解的文章会越来越少(体会到了举办一场 CF 比赛的出题组还要写题解的艰辛),到时候解题报告也只会提及一些重点题目,也许在某种意义上算是偷懒,但确实会节省时间 qwq,目前每道题会尽量写清楚思路,但如果感觉写清楚会很啰嗦或遇到需要感性理解的地方会在某种程度上模糊处理。

为了提高阅读体验,特意将所有代码以 github 链接而不是长长的代码块的形式展现,希望这种形式可以使页面更加清爽。

由于比较菜,后两题是向其他选手学习后才写出的qaq

话不多说,让我们开始吧 qwq


CF1559A. Mocha and Math

Description

给定一个长度为 \(n\) 的整数序列 \(a_1, a_2, \cdots, a_n\),每一次你可以选定一个区间 \([l,r]\),然后将序列中 \([a_l, a_{l + 1}, \cdots, a_{r - 1}, a_r]\) 替换为 \([a_l\,\& \,a_r, a_{l + 1}\,\&\,a_{r - 1}, \cdots, a_{r - 1}\,\&\,a_{l + 1}, a_r\,\&\,a_l]\),此操作使用次数不限,要求通过操作使这个序列的最大值最小,并输出这个最大值的最小值。多组数据。

数据范围:\(n \leq 100,a_i \leq 10^9,\text{数据组数 }t \leq 100\)

Solution

我们不难发现对于按位与这个运算 \(a\,\&\,b \leq \min(a, b)\),这是因为运算中只有 \(a, b\) 二进制同为 \(1\) 的位才会被保留为 \(1\),其他位都会变成 \(0\),所以进行按位与后,数的大小是单调不增的。

我们发现如果每次考虑一个较长的区间必然使问题复杂化,而操作次数又没有限制,我们于是考虑每次只选择长度为 \(2\) 的区间,显然由按位与单调不增的性质,很容易发现最终序列所有数可以变成相同的数,而这个数就是我们要的最小值。而最终的数都是由序列中的数按位与得到的,所以只需考虑用序列中的数按位与如何得到最小的数即可,显然只需让序列中的所有数按位与在一起即可。即:\(a_1\,\&\,a_2\,\&\,\cdots\,\&\,a_n\)

Code

View on github


CF1559B. Mocha and Red and Blue

Description

给定一个长度为 \(n\) 的,由 R,B 组成的序列,如果序列中相邻两个位置的字母相同,那么这对位置称为“不完美的”,定义一个序列的不完美度为这个序列不完美位置的对数,现在给定的序列有一些未填上字母的位置(用 ? 表示),要求将这些位置填上字母后使得此序列的不完美度最小。多组数据,答案可能不唯一,输出任意一种即可。

数据范围:\(n \leq 100,\text{数据组数 }t \leq 100\)

Solution

我们发现对于一个串,让我们填充的部分情况无非等价为:空串,给定了开头或结尾的串,同时给定开头或结尾的串。

对于一个空串,我们只需填充让 R 与 B 交替出现即可保证其不完美度最小(即类似于 "RBRB·····")。

对于一个给定开头或结尾的串,我们只需依照空串的策略,让 R 与 B 交替出现即可。

然而对于一个同时给定开头或结尾的串,我们发现如果开头和结尾两个字母相同,如果头尾之间有奇数个空位,我们使用先前的策略就可以使这段的不完美度为 0,若有偶数个空位,这段的不完美度最小也只能为 1,显然要达到最小,仍然可以交错填充 R 与 B。

综上我们只需先特判给定串开头和结尾是不是空位,后按照上述情况对应的方法填充即可。

Code

View on github


CF1559C. Mocha and Hiking

Description

给定一张 \(n + 1\) 个节点 \(2n - 1\) 条边的有向图,图上的边分为两种:

\(\circ\)\(n - 1\) 条边为从节点 \(i\) 出发到节点 \(i + 1\),其中 \(1 \leq i \leq n - 1\)

\(\circ\) 另外 \(n\) 条边用 01 序列 \(a_1, a_2, \cdots, a_n\) 表示,其中若 \(a_i = 0\),则说明有一条从节点 \(i\) 到节点 \(n + 1\) 的边,若 \(a_i = 1\),则说明有一条从节点 \(n + 1\) 到节点 \(i\) 的边。

请找出一条经过所有节点且每个节点只经过一次的路径,路径的起点和终点没有限制,如果无解请输出 -1,多组数据。

数据范围:\(\sum n \leq 10^4,\text{数据组数 }t \leq 20\)

Solution

发现题目中第一类边比较特殊,如果我们从节点 1 出发可以之间走到节点 \(n\),所以我们只需考虑如何到达节点 \(n + 1\) 即可。当然,比较容易想到的特殊情况是直接有一条从节点 \(n\) 到节点 \(n + 1\) 的边或有一条从节点 \(n + 1\) 到节点 1 的边,这两种情况答案非常显然,不再赘述。

考虑以上两种情况都不满足时应该怎样做,直觉上告诉我们只需找到一个节点 \(i\),其满足 \(i\)\(n + 1\) 有一条边,且 \(n + 1\)\(i + 1\) 有一条边即可,这样我们就可以将 \(n + 1\) 直接插入到节点 1 到节点 \(n\) 的路径上(或者更形象一点,我们通过节点 \(n + 1\) 由节点 \(i\) “中转” 到了 \(i + 1\))。

考虑还有没有其他有解的情况,发现由于每个节点只能经过一次,所以如果到达过 \(n + 1\) 后就不可以再通过其“中转”到其他节点了,导致如果我们从节点 \(i\) 经过节点 \(n + 1\) 再回到一个节点 \(j\,(j > i + 1)\) 时,我们至少永远也不可能走到节点 \(i + 1\) 了,这显然不符合题目要求,故如果上文提到的有解的条件都不满足,就一定无解。

Code

View on github


CF1559D1. Mocha and Diana (Easy Version)

Description

给定两个 \(n\) 个节点的森林(每个节点不一定相互连通的无向无环图),其初始边数分别为 \(m_1, m_2\),现在有一种加边操作,其要求为:

\(\circ\) 设加入的边为 \((u, v)\),则这两个森林都会将节点 \(u\) 和节点 \(v\) 连起来;

\(\circ\) 保证加边后,两张图仍然为森林(即不会产生新的环路)。

求最多可以加多少边,并输出任意一种可以加最多边的方案。

数据范围:\(n \leq 1000,\,m_1,m_2 < n\)

Solution

由于初始给定的图为森林,所以只要我们加边时的两个点在加边前的两个森林中均不连通即可,所以我们考虑使用并查集来维护点与点之间的连通性。

发现 \(n \leq 1000\),所以 \(O(n^2 \alpha(n))\) 的时间复杂度完全可以通过本题,故直接考虑暴力的枚举两个点之间是否可以加边即可。这样加边到不能再加就一定到达了最大值。

Code

View on github


CF1559D2. Mocha and Diana (Hard Version)

Description

(本题题面与上题相同,仅 \(n\) 的范围有变)

发生变化的数据范围:\(n \leq 10^5\)

Solution

由于 \(n\) 最大到达 \(10^5\),所以我们不能用 \(O(n^2 \alpha(n))\) 的暴力通过,考虑其他方法。

我们不妨将一个点作为特殊点(这里选取了 1 号点),先判断其他节点在两个森林中与 1 号点是否联通,若不连通就向 1 号点连边。

在加完这些边后我们发现只剩下三类点:

  1. 在两个森林中均与 1 号点连通;
  2. 在第一个森林中与 1 号点连通,在第二个森林中与 1 号点未连通;
  3. 在第一个森林中与 1 号点未连通,在第二个森林中与 1 号点连通。

其中可能可以加边的只有第 2、3 类点,并且显然这两类点之间是不连通的(否则他们都会和 1 号点连通),所以我们考虑将两类点分别放到两个队列中,每次两个队列各取出一对点进行加边操作,并在加边后让不再符合两类点定义的队头直接弹出,以保证加边的合法性,直至其中一个队列空后,加边结束。由于最后第 2、3 类点中最多只剩一类点,所以不可能再加更多的边,所以最终得出的即为最大加边数及对应的方案。

而时间复杂度似乎比较离谱,为 \(O(n \alpha(n))\)

Code

View on github


CF1559E. Mocha and Stars

Description

有一个长度为 \(n\) 的整数序列 \(a_1, a_2, \cdots, a_n\),求同时满足以下条件的不同序列有多少种:

\(\circ\) \(\forall i \in [1, n], a_i \in [l_i,r_i]\)

\(\circ\) \(\sum_{i = 1}^n \limits a_i \leq m\)

\(\circ\) \(\gcd(a_1, a_2, \cdots, a_n) = 1\)

其中 \(n, l_i, r_i, m\) 已给定,两个序列 \(a_1, a_2, \cdots, a_n\)\(b_1, b_2, \cdots, b_n\) 被认为是不同的,当且仅当存在 \(i \in [1, n]\) 使得 \(a_i \neq b_i\)

数据范围:\(2 \leq n \leq 50, m \leq 10^5\)

Solution

日常的反演与计数题

首先只考虑前两个条件,可以直接使用背包计数来做。设 \(f(i, j)\) 表明考虑填充序列中前 \(i\) 个数,其和为 \(j\) 时的方案数,不难写出方程:

\[f(i, j) = \sum_{k = l_i}^{r_i} f(i - 1, j - k) \quad (f(0, 0) = 1) \]

其中 \(i\) 那一维可以滚动数组优化掉,而每次转移都是取 \(f(i - 1, j - r_i)\)\(f(i - 1, j - l_i)\) 这一段连续的值,故可以使用前缀和优化转移。

再考虑加上第三个条件,不难发现答案为:

\[\begin{aligned} & \sum_{a_1 = l_1}^{r_1} \sum_{a_2 = l_2}^{r_2} \cdots \sum_{a_n = l_n}^{r_n} \left[\sum_{i = 1}^n a_i \leq m\right] \left[\gcd(a_1, a_2, \cdots, a_n) = 1\right] \\ = & \sum_{a_1 = l_1}^{r_1} \sum_{a_2 = l_2}^{r_2} \cdots \sum_{a_n = l_n}^{r_n} \left[\sum_{i = 1}^n a_i \leq m\right] \sum_{d \mid gcd(a_1, a_2, \cdots, a_n)} \mu(d) \\ = & \sum_{a_1 = l_1}^{r_1} \sum_{a_2 = l_2}^{r_2} \cdots \sum_{a_n = l_n}^{r_n} \left[\sum_{i = 1}^n a_i \leq m\right] \sum_{d \mid a_1, d \mid a_2, \cdots, d \mid a_n} \mu(d) \\ = & \sum_{d = 1}^{m} \mu(d) \sum_{a_1 = \lceil \frac{l_1}{d} \rceil}^{\lfloor \frac{r_1}{d} \rfloor} \sum_{a_2 = \lceil \frac{l_2}{d} \rceil}^{\lfloor \frac{r_2}{d} \rfloor} \cdots \sum_{a_n = \lceil \frac{l_n}{d} \rceil}^{\lfloor \frac{r_n}{d} \rfloor} \left[\sum_{i = 1}^n a_i \leq \left\lfloor \frac{m}{d} \right\rfloor\right] \end{aligned} \]

其中前面的 \(\sum_{a_1 = l_1}^{r_i} \limits \sum_{a_2 = l_2}^{r_2} \limits \cdots \sum_{a_n = l_n}^{r_n} \limits [\sum_{i = 1}^n \limits a_i \leq m]\) 正是对前两个条件的描述,第一步转化的根据是由莫比乌斯函数 \(\mu(n)\) 的定义得出的经典等式 \(1 * \mu = \epsilon\),第三步是将 \(d\) 放到前面枚举的经典套路。

最终的式子后面可以直接用上文所述的背包计数 + 前缀和优化解决,总复杂度 \(O(n \sum_{i = 1}^m \limits \lfloor \frac{m}{i} \rfloor) = O(nm \log m)\)

Code

View on github

posted @ 2021-08-17 18:38  Nickel_Angel  阅读(117)  评论(0编辑  收藏  举报