[CodeForces] CF558 题解
注:难度评级为 D 到 A,对标 NOIP T1 到 T4。+ 表示比原本难,- 反之。例如,D+ 比 D 难。难度评级仅供参考。 如果认为难度评级与实际难度不符,可以在评论区@我进行讨论。
本篇题解无复杂的公式推导,题目较清新自然,请放心食用。
斜体字为说明提示。通常与多倍经验有关。
A. Lala Land and Apple Trees
【难度分析】
思维难度:D-
实现难度:D-
总体难度:D-
评级: 普及-
【题目大意】
一个人,最初在数轴 \(0\) 点,每次可以选择一个方向走,走到苹果树的位置 \(x_i\),获得 \(a_i\) 价值,然后掉头。一直走,直到他走到方向没有苹果树。一共有 \(n\) 颗苹果树。
- \(1 \leq n \leq 100\),\(-10^5 \leq x_i \leq 10^5\),\(x_i \neq 0\),\(1 \leq a_i \leq 10^5\)。
【解题思路】
考虑贪心。
显然,应该选择苹果树较多的那个方向走。最后得到的苹果总数是 \(\min(左边个数,右边个数) + 1\)。使用 vector
存储信息,排序后统计即可。
B. Amr and The Large Array
【难度分析】
思维难度:D-
实现难度:D-
总体难度:D-
评级: 普及/提高-
难度解析:此题的难度瓶颈在于理解题意。
【题目大意】
注意:洛谷上的题目翻译不准确。
定义一个序列的美丽值 \(f(A)\),为在序列 \(A\) 中,出现次数最多的数的出现次数。
形式化地,有
其中,\(n\) 是序列 \(A\) 的长度。\([]\)(方括号)表示,如果它内部的表达式为真,那么值为 \(1\),否则为 \(0\)。
现在,给定你序列 \(A\),你需要求出 \(A\) 的一个连续的子段 \(B\),使得 \(f(A) = f(B)\)。如果 \(B\) 在 \(A\) 中的位置为 \([l, r]\),你只需要输出 \(l\) 和 \(r\)。
- \(1 \leq n \leq 10 ^ 5\),\(1 \leq a_i \leq 10 ^ 6\)。
【解题思路】
容易知道,我们需要选取出现次数最多的数,所以只需要枚举出现次数最多的数。由于我们要使答案最小,我们可以取 \(l\) 为这个数最左端的位置,\(r\) 为这个数最右端的位置,这样一定是长度最小的。
C. Amr and Chemistry
【难度分析】
思维难度:C+
实现难度:C-
总体难度:C
评级: 普及+/提高
难度解析:此题对标三值逻辑。
【题目大意】
有 \(n\) 个数,第 \(i\) 个数为 \(a_i\),你可以对每个数进行一下操作:
- 将一个数乘二。
- 将一个数除以二并向下取整。
问最少通过多少次操作可以将这些数变得相同。
- \(1 \leq n \leq 10 ^ 5\),\(1 \leq a_i \leq 10 ^ 5\)。
【解题思路】
第一个操作相当于在数的二进制的末尾补 \(0\),第二个数相当于去掉末尾的数。
这样,我们可以直接使用 01-Trie。末尾补 \(0\) 相当于向 \(0\) 儿子走,去掉末尾相当于走到父亲节点。
于是问题变成:一棵树上,每次可以走向 \(0\) 儿子或者父亲,你需要确定一个点,使得所有有标记的点到这个点走的步数和最小。这里的有标记的点是指 \(a_i\) 的末尾在 01-Trie 上的编号。
那这就是一个经典换根 DP 了。设 \(f_i\) 为所以有标记的点走到 \(i\) 的最小步数,\(g_i\) 为 \(i\) 子树内有标记的点的个数,那么有
解释:其中,\(v\) 为 \(u\) 的子节点。我们 \(v\) 的子树内所有节点,共 \(g_v\) 个,从走到 \(u\) 变成了走到 \(v\),那么显然少走了一步,要减去 \(g_v\)。但其他节点都多走了一步,所以要加上 \(n - g_v\)。
其实这种换根 DP 技巧与这道题完全一样。
实现方法:第一次 DFS 求出 \(g\),第二次 DFS 求出 \(f\)。第二次 DFS 只能向 \(0\) 儿子递归,因为只能末尾补 \(0\) 不能末尾补 \(1\)。且第二次 DFS 时,根节点不是原来字典树的根节点。这个根节点是所有节点的 LCA。为什么?因为变到 LCA 就不用变了。
注意 \(a_i\) 可重复。注意根节点的 \(f\) 的初始值。
D. Guess Your Way Out! II
【难度分析】
思维难度:B
实现难度:C+
总体难度:B-
评级: 省选/NOI-
【题目大意】
一颗完美二叉树,根节点编号为 \(1\)。第 \(i\) 个点左儿子编号为 \(2 \times i\),右儿子编号为 \(2 \times i + 1\)。根节点为第一层,儿子节点的层数在它的父亲节点的基础上增加 \(1\)。
你需要求出一个叶子节点(叶子节点层数为 \(h\)),给你 \(q\) 个信息:
每次信息形如 \(i, L, R, o\),表示该节点的层数为 \(i\) 的祖先的编号是否在区间 \([L, R]\) 内,是则 \(o = 1\),否则 \(o = 0\)。
如果无法找到一个这样的节点,输出 Game cheated!
;如果有多个这样的节点,输出 Data not sufficient!
;如果有且仅有一个这样的节点,输出节点编号。
- \(1 \leq h \leq 50\),\(1 \leq q \leq 10 ^ 5\),保证信息合法。
【解题思路】
考虑到在树上进行区间操作是一件很困难的事,所以应该把它转化到序列上。
具体来说,给定深度为 \(i\) 的区间 \([L, R]\),容易求出它在叶子节点那一层对应的区间。现在问题变成了:给定若干个区间,告诉 \(x\) 在不在区间内,让你求 \(x\)。
对于“在”区间内的情况,直接全部取交集即可。对于“不在”的情况,先按左端点排序,这样会方便考虑,然后逐一合并相交的区间,再取补集,最后与“在”的区间合并即可。
注意判断无法找到的情况、有多个的情况还有没有“不在”区间的情况。
E. A Simple Task
【难度分析】
思维难度:D+
实现难度:B
总体难度:C+
评级: 提高+/省选-
【题目大意】
给定一个长度为 \(n\) 的,仅由小写字母组成的字符串 \(S\),有 \(m\) 次操作,对于每次操作:
- 将 \(S_l \sim S_r\) 升序排序。
- 将 \(S_l \sim S_r\) 降序排序。
求最终的字符串。
- \(1 \leq n \leq 10 ^ 5\),\(1 \leq m \leq 5 \times 10 ^ 4\)。
【解题思路】
定义函数 \(f(x)\),表示在字母表中排名为 \(x\) 的字母;\(g(c)\) 表示字母 \(c\) 在字母表中的排名。
由于只有 \(26\) 种字符,考虑对每种字符都建立一颗线段树,这样我们需要 \(26\) 颗线段树。
具体的,第 \(i\) 颗线段树维护字母 \(f(i)\) 的位置(和区间内的出现次数)。这样,根据线段树的叶子节点存储的信息可以计算每个位置的字符。
考虑排序操作,这里只讨论升序排序,因为降序排序同理即可。
我们观察一个例子:对于 acababc
进行排序,结果是 aaabbcc
。这里,第 \(1\) 到 \(1 + 3 - 1\) 位置是 a
,第 \(4\) 到 \(4 + 2 - 1\) 位置是 b
,第 \(6\) 到第 \(6 + 2 - 1\) 位置是 c
。注意到这里的 \(4\)、\(2\)、\(2\) 是这些字符在区间内的个数。于是我们就有了一种方案:
每次修改时,从小往大枚举字符,这样就可以保证升序。对于字符 \(i\),我们将第 \(g(i)\) 颗线段树 \(p + 1\) 到 \(p + cnt + 1\) 修改为 \(1\),其他修改为 \(0\)。其中,\(p\) 为上一个字符的末尾,\(cnt\) 为这个字符在修改的区间内的出个数。这样就可以完成的排序的操作了。
这里,我们利用了两个性质:排序后相同的字符是连续的;排序操作不影响区间内字符的个数。
输出的时候,我们遍历时遍历到叶子节点输出即可。
时间复杂度 \(O(26\) \(m \log(n))\)。
其实此题与这个题有着较为相似的思路,只不过后者将值域从 \(26\) 扩大为正整数,需要二分答案,略难一些,有余力的读者可以尝试一下。