比赛地址:牛客周赛 Round 64
出题人:神崎兰子
A 小红的对错判断
把所有字符都转换成大写形态,将字符串与 比较。
参考代码
B 小红的幂表达
从大到小枚举底数 ,求出对数 。
如果有 ,输出之。
由于底数从大到小枚举,幂次会是递增的。
对于求对数的环节,计算机并没有对每个 都有 函数。
所以我们使用换底公式 。
参考代码
C 小红的前缀询问
使用 dict 维护当前位置 之前,有多少个元素等于 。
设 是存放元素数量的 dict,则第 个位置的贡献为 。
我们记这个贡献为 ,则前缀 的贡献和为 。
在此基础上,对数组 做前缀和,即可快速求解区间之和。
参考代码
D 小红和小紫的博弈游戏
考虑四种组合 , , , 。
如果没得取,只有两种可能, ,或者 。
既然是最优策略,那么都会取最小的那个。
参考代码
E 小红的字符串重排
首先想到一个 trick ,最大的同种字符数量 与 的关系。
这题要求每个位置都不能放原来的字符,所以需要满足 。
如果满足,我们考虑字符集整体偏移 个长度。
首先,构造一个长度为 的二元组数组,每个二元组的 first 存放字符,second 存放下标。
然后,按照字符大小进行排序,从小到大或者从大到小都行,这里是为了让同样字符放到一块去。
然后,构造一个存目标字符的数组 ,对于每个二元组,将偏移过后字符放入新的下标。
最终,将字符数组转换为字符串,输出。
参考代码
F 小红的树上路径查询(easy)
由于 ,我们可以考虑时间复杂度为 的算法。
定义 为 到 的路径,那么我们需要找出 上所有的点,并且求出其他点到 的最短距离。
现在大问题变成了两个小问题。
1.找出路径上的点。
假设点 在路径上,那么从 到 的路程中,必然经过点 ,则有 。
注意到,这些边都是无向边,有 。
那么上式可转换为 。
是 到 的距离, 是 到 的距离。
那么,只要把 当作起点,做一次 bfs ,即可得知 到其他点的最短距离, 同理。
做完两次 bfs ,分别得到每个点与 的距离数组 。
如果点 有 ,那么 就是在路径上的点。
2.求出其他点到 的距离。
假设有极端情况,一半的点在路径上,另一半不在。
对所有在路径上的点,分别做一次bfs,然后对每一个非路径点,比较得出最小值,求和。
时间复杂度达到了惊人的 ,不可取。
那么多次 bfs ,我们是否能只做一次 bfs 呢?
你别说,还真有。
我们创建一个虚拟源点(也叫超级源点),连接它和路径上的所有点,边权为 。
这个源点就是只用做一次 bfs 的起点,它将时间复杂度降到了 。
在代码实现时,我们不需要真的创建这个点,以及对应的 边。
只需要在初始化队列时,把路径上面的点都扔进去就好(普通bfs只会扔一个点进去)。
bfs 结束之后,把距离数组的值全部加起来,输出。
参考代码
G 小红的树上路径查询(hard)
hard version 的 ,显然 的做法也倒闭了。。。
让我们重新审视点与路径的关系。
点在路径上的情况,已于 F 题解中说明。
那么,点不在路径上的情况呢?
假设点 不在路径上,离它最近且在路径上的点是 。
路径的两端仍然是 和 。
那么,从 到 或者 从 到 的路程中,必然经过点 。
所以有如下推导:
对于在路径上的点,代入上式,也是正确的。
因为此时 , 。
所以,对于给定的所有点,都满足上式。
有同学会问,我们知道这个有什么用吗?
别急,我们来反推一波。
我们想求的是全部的 之和,注意,这里并不需要求出具体的 。
根据上式,移项可得 。
对所有 的求和为:
相信大家已经看出来了
是点 到所有点的距离之和。
是点 到所有点的距离之和。
是点 到点 的路径长度。
大问题又变成了两个小问题。
和 可以使用 换根dp 维护。
可以使用 LCA 维护。
对于具体算法,不在此处赘述。
对于单次询问的时间复杂度,换根dp 的复杂度是 ,LCA 的复杂度是 ,总复杂度是 。
需要注意的是,python 在递归上的效率奇差,需要使用栈来替代递归。
参考代码
感谢阅读!
全部评论
(0) 回帖