idea, data, solution, std from Ecrade_
引理 1:不移动到子树内没有黑点的结点一定不劣。
我们的目标是遍历到所有黑点,而移动到子树内没有黑点的结点只会徒增操作次数。
引理 2:当移动到结点 的子树内时,遍历完 子树内的所有黑点再移动到 的子树外一定不劣。
若未遍历完 子树内的所有黑点就移动到了 的子树外,则后面仍需再次移动到 子树内遍历剩下的黑点,而这显然只会徒增操作次数。
引理 3:去除子树内没有黑点的结点后得到一棵新树,那么沿着新树的某种欧拉序移动一定不劣。
不妨令红点表示当前未遍历完子树内所有黑点的结点,绿点反之。
根据引理 1,去除子树内没有黑点的结点不会造成任何影响。
根据引理 2,若当前棋子在红点处,则棋子只能往下移动(即移动到深度更大的结点处)。
若当前棋子在绿点 处,则考虑离 最近且为红点的祖先结点 。
根据引理 2,棋子接下来只能移动到 子树内的某个黑点。
观察到在移动的过程中,棋子必然会经过 或 的某个子结点。
那么在从 移动到 或 的某个子结点的过程中,由于路径上除了 为红点,其余均为绿点,故一直往上移动是最优的。
接着,在到达 或 的某个子结点后,棋子必然又在红点处,这时只能往下移动了。
移动到最后一个黑点后,显然我们一直往上移动至根节点即可。
所以,在整个过程中,我们发现棋子是不会走“回头路”的,其实这也是沿着欧拉序移动的充要条件。
引理 4:沿着新树的欧拉序移动一定是所有移动路径中总路程最短的。
显然,我们必须要经过所有新树的叶结点。
所以,每条边都需要被经过至少两次,而沿着欧拉序移动恰能达到这个理论下界。
注意到总路程 为定值(新树边数的两倍),若第二种操作进行了 次,则总操作次数为 。
所以,我们仅需知道 在无限制的情况下的最大值 即可,这样对于每个询问,答案即为 。
考虑使用动态规划计算出 取到最大值时的操作次数。
定义 表示进入结点 的子树内时,棋子处在 的某个子结点处;遍历完 子树内所有黑点后,将要从 的某个子结点移动到 子树外的操作次数。
由于 和 的情况是对称的,故我们仅考虑 。
对于 的所有子结点,我们需找到一个合适的遍历顺序,使得依次拼接后操作次数最少。
容易发现,相邻两个子结点分别是 出和 进时,则中间只需进行一次操作,而其余情况则需进行两次操作,故一个直观的想法是 越多越好。
考虑贪心。
一般情况下,当 时,取 不劣。
一般情况下,当 时,取 不劣。
和 同理。
所以我们得出结论:一般情况下,对于 ,哪个最小就取哪个,若有多个最小值,则哪个含 越多就取哪个。
至于这里的结论为什么只适用于一般情况下,后续会提及。
子结点与子结点之间的拼接情况已处理完,最后仅剩下首尾两处根结点与子结点之间的情况了。
显然,当根结点 和子结点 分别满足 进和 进或是 出和 出,即进/出 子树时处在的结点恰为 时,便不需要进行操作,而其余情况则需进行一次操作。
仍考虑贪心。
首尾两处与子结点均需进行一次操作,而中间相邻子结点之间分别满足 出和 进的数量越多越好。
当没有 进 出或 进 出的子结点时,只能将所有 进 出的子结点紧挨着排,后跟着所有 进 出的子结点;
否则将一个 进 出的子结点排在首位,后跟着所有 进 出的子结点,再跟着 进 出、 进 出的交替排列,最后跟着所有 进 出的子结点。
将所有 进 出的子结点紧挨着排,后跟着 进 出、 进 出的交替排列,最后跟着所有 进 出的子结点。
若 仅有一个子结点,暴力考虑所有情况即可;
否则将所有 出 进的子结点紧挨着排,后跟着 出 进、 出 进的交替排列,再后跟着所有 出 进的子结点,最后跟一个 出 进的的子结点。
注意这里需要再考虑强制将所有子结点变为 进 出的子结点的情况,因为这时的贪心可能就不奏效了。注意此时若 为黑点则需中途再进行一次操作去遍历 。
至此,我们便用 时间复杂度解决了本题。
idea, data, solution, std from JohnVictor
算法一(暴力)
直接 ,复杂度 ,可以通过第一个子任务。
算法二(部分分)
输出 可以通过第二个子任务。第三个子任务的答案可以用一些组合数简单表示。对于第四个子任务,可以看做 个点加一个下三角,下三角部分的贡献可以简单反射容斥计算,剩下部分暴力即可。第五个子任务留给小常数 的直接分块 和大常数正解。
算法三(正解)
下面的分析中,令块长 。
考虑平移指数基。具体地,对于每一列,考虑维护一个形如 的多项式表示一整列的 值构成的生成函数。
我们要支持的操作是加一个单项式,查询一项系数(这两个操作足以解决每一列有一个不能走的点的问题),将 加上 (也就是一次前缀和)。
考虑定期重构。我们维护 的形式。每一次将 减小 ,变成了 就重构。这样,对于这个形式查询一项系数是 的。
利用 ,重构的时候只用除以 ,这个相当于隔 个做一次前缀和,复杂度 。
主要的问题是加一个单项式之后再除以 怎么办。这个只用额外维护成 即可。注意这里的系数还是可以 来计算的,因为 不超过 个非零系数,单个贡献可以 。
所以现在的复杂度为 ,可以通过所有子任务。
算法四
考虑每 条斜线(即 的位置 )算一次 dp 值。转移的时候可以考虑对障碍点容斥,钦定一些障碍点被经过了,并对可能产生转移的两个障碍点之间计算贡献。
斜线 -> 斜线的贡献可以 处理;障碍点 -> 障碍点,斜线 -> 障碍点和 障碍点 -> 斜线的贡献均可以 处理。
时间复杂度 。
idea, data, solution, std from zhoukangyang. sol-114514,-1919810 from bulijiojiodibulido
APIO2024 会讲的。大家可以提前复习一下积性函数相关知识。UPD:apio 讲课的核心内容放在了 这里
算法 -114514,-1919810 是 bulijiojiodibulido 老师验题时用的做法,算法一、算法二是官方题解做法。
算法 -114514
令 ,如果我们提取 和 中与 相关的所有因子,即令 ,其中 。那么有 。
所以可以考虑枚举 ,满足它们有相同的素因子分布(即 ,其中 ),然后计算上式。这个时候我们还有 的条件,如果直接反演,那么复杂度比较高。
类似构造系数 ,满足 。令 。那么答案为 。因为我们能保证每对 ,前面加的总系数为 。
对于函数 ,在上面等式中,右侧是积性,也就是关于每个素因子是独立的,所以 的取值也是对于每个素因子都是独立的。所以只需要对于所有素数,预处理出 的值(事实上是一个次数为 的多项式),那么对于任意两个数,可以快速算出上述的 值。
这个时候直接枚举 ,也就是从小到大枚举每个素数,以及在 中的指数就能快速计算答案。枚举代价为 到 内所有 相同的数字的对数,好像是 级别的,可以通过 的数据。
算法 -1919810
考虑如何加速这个过程,我们希望能快速计算出比较大的素数的贡献。
表示从大到小枚举因子到 ,, 的乘积。对于大于 的素数,它一定是从 转移向 ,并且乘上的是一个四次多项式。
那么我们只需要预处理 相同的每段的素数的四次多项式的值,就可以快速完成这些素数的转移。对于剩下的素数,我们枚举它们的指数,从大到小转移即可。
如果使用记忆化,当 的时候,状态数大概在 级别,转移大概在 级别,使用精细的实现卡常数就可以通过。
实际大部分的状态都是在小素数的地方,所以可能可以从小素数枚举然后 meet in the middle,但是没编出来。
算法一
考虑把积性函数推广至二维,如果函数 是二维积性函数,假设 是从小到大第 个质数,,,那么 。容易发现我们要求的是二维积性函数前缀和。
考虑设计积性函数 满足 。对于这个函数,其前缀和 是相当容易计算的。观察 。我们发现 。
暴力枚举 有值的位置,可以证明复杂度是 。期望得分 分。
算法二
考虑优化之前的算法,设计二维积性函数 满足 只在 处有值且值为 ,接下来令 。
此时有值的 (,)就满足 且 了。
接下来暴力枚举 中的有值元素 ,计算 ,把这些答案带上系数加起来就能得到最终答案了。
而 。暴力整出分块计算答案。
我们当然要筛 和 的块筛前缀和。可以证明,除此之外的复杂度不超过 。
其实还可以更快一点,不过没必要了。