Codeforces Round 873 (Div.1)
A. Counting Orders
对于每个 ,可以计算出 表示有多少个 。
那么第 个人就有 种可能的位置。
注意到如果将 升序排序,则能放的位置集合从前往后是包含的关系。
所以将 排序(等价于将 排序)后计算 即可。
时间复杂度 。
B2. Range Sorting(Hard Vision)
考虑研究一个序列的权值如何计算。
首先把 相同的再根据出现位置定义大小关系,这样就转成了 的排列:容易发现答案不变。
如果两个操作区间有交,不妨合并成一个,答案不会更差。
所以我们相当于是把原序列划分成若干段,每段进行一次排序操作。
可以看出权值就是 ,其中 是你划分的段数。
所以我们要让划分的段数尽可能的多;划分的条件是要保证每一段之间都是有序的。
换言之如果我们断开了 和 ,那么 的最大值要小于 的最小值。
现在回到原问题来,我们实际上是要统计:每个子区间的分段次数和。
考虑一个 的位置,有多少个子区间经过它且在此分段。
我们考虑从小往大激活 中的每个位置,则 合法()当且仅当有一个时刻我们激活了 ,而 全部没有被激活。
我们可以让 在激活了 最大值的那个时刻被统计到。
所以当我们本次激活 的时候,考虑其所在连通块 (),则 的取值在 范围; 的取值在 范围,其中 是 后面第一个已经被激活的位置。
求连通块 可以用并查集维护,而寻找 可以用 std::set<int>
维护。时间复杂度 。
C. Palindrome Partition
考虑如何判定一个串是否合法。
结论是我们贪心地划分就行。
如果我们不贪心地划分而仍然得到了解,则说明存在一对 满足长度为 的前缀都回文,假设 是最小的回文前缀。
当 的时候,注意到 可以分成开头 个;结尾 个,中间长度为 的一段;显然这三段都是回文的。
当 的时候, 的前 个和后 个有交为 的公共段;所以这一段是回文的,那么最开始的前 个也是回文的;而 ,所以这和 是最小回文前缀矛盾。
感觉不如.... Double Palindrome
回到原题:考虑设 是最小的 满足 是偶回文串,当求出了 以后连 ,则题目所求等价于这张图上的路径条数(七点终点不同),这个很容易在 的时间内 出来。
然后考虑求 。考虑设 为回文中心能拓展到的长度是 ,则相当于求最小的 满足 。
枚举 ,则相当于把 这个区间内还没有确定 的位置,把他们的 全部设为 ,这个过程容易用并查集维护。
时间复杂度 ,瓶颈在于用哈希 + 二分求 。当然使用 可以做到 。
D. Two Centroids
一棵树如果要有两个重心,首先它们必须相邻;然后考虑断掉他们之间的连边,两边的连通块大小必须完全一致。
这对树的形态做出了很强的约束。我们可以 解决树形态固定的情况:枚举一个点 ,那么两个重心是 的答案就应该是 。
回到动态加叶子的情况,首先这个形式可以看成一颗确定的树,每次激活一个点。
这样利用 可以做到 求某个时刻的一个位置的子树大小 。
注意到二者必定有一个是树本身的重心,否则我们把它们同时往重心移动一步,小的连通块变大大的连通块变小且依旧满足小的连通块 大的连通块,答案一定更优秀。
所以我们先考虑求出每个时刻的重心,这个很好做,我们加入一个叶子,那么原重心这个方向的连通块大小就增加了 ,如果它大于了一半就往这个方向移动一步就好了。
然后现在考虑问题变成了每次给一个点 问当前 的所有子树(无根树意义下)里 最大的那个(显然这样加的点最少)。
考虑以 为根的话,父亲的那个部分是容易 直接算出的,关键是要找到它的重儿子是哪个。
考虑加入一个点 ,重儿子可能会更改的肯定是 链上的某个点;且这条链上最多只有 个点是父亲的轻儿子(根据轻重链剖分的理论),显然本来是重儿子的还会是重儿子,所以只用看这些轻儿子是否变成了新的重儿子即可,那么这个部分是单次 的,现在考虑如何去找这些点。
也就是抽象成:树上激活/熄灭一个单点,找到根链上的所有激活点。
如果是序列,则根链变成前缀,然后考虑可以维护 std::set<int>
并在其上二分查找;对于树上,我们树剖以后对 个区间都做这样的事情,复杂度是均摊 的(每个激活点贡献 的查找开销)。
这样时间复杂度 ,可以预料到 是无法通过的,把 std::set<int>
改成树状数组上二分即可,这样常数小了非常多,在 秒内都可以通过。
当然,这并不是正解;我们 rewind 一下,回到求重心 的做法。
假设我们知道了加入新点之前的 和重儿子大小(包括父亲那部分也考虑在内),则考虑新点对应的那颗子树,如果我们的 没有变,那么考虑用它的大小更新重儿子大小;否则我们知道重儿子大小就是 ( 是当前加入点的数目)。
所以只用一个 和一次 其实就够了,时间复杂度 。
E. Bus Routes
首先可以看出,我们只研究叶子和叶子之间的可达性。
然后考虑把 的特判掉,当 的时候总能找到一个非叶子点 ,以 重新定根。
考虑两个叶子 ,要么有一条路径 ,要么必须中转一次。无论如何 处都会被经过到。
所以考虑枚举 ,然后考虑所有 的叶子点对 。
如果两个人为起点都存在一条路径经过 ,显然它们可以中转到一起;如果都不存在显然无法中转到一起;如果只有一个人的路径能经过 ,则相当于是这样一个形态: 经过 然后向下往 走, 的路径向上够;最后两人回合。
发现第一种情况可以视为第三种情况的特例。
因此,我们对每个叶子找到最浅的 满足叶子到 有一条路径,问题变成了查询是否 子树外的叶子到 都有一条路径。
可以先对每个叶子找到这个 ,然后统一处理询问。
考虑用叶子去覆盖这些询问点,则一个考虑叶子 的一条路径 ,它需要覆盖到 的路径上所有不是 祖先的点。
考虑树剖:首先 的路径被剖分成了 段,所以不在这条路径上的点集也被剖分成了 段,记作 ;每条从 出发的路径根据树剖划分成 段,把他们合并成若干个不交的区间,然后分别给 集合的每一段做贡献。贡献是区间加所以打差分就好了。
时间复杂度 ,因为没有用数据结构所以常数不大,可以通过。
F.Copium Permutation
我还没有想出来。
可能想不出来了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现