2025.12 北京多校集训

2025.12 北京多校集训

根号数据结构

P9809 [SHOI2006] 作业

\(Y\) 根号分治,当 \(Y\le B\) 的在插入时枚举 \(Y\) 并更新最小值;当 \(Y > B\) 的枚举 \([kY,(k+1)Y)\),二分求出区间中的最小值。复杂度 \(O(n\sqrt V\log n)\)

CF1822G2 Magic Triples(Hard Version)

\(b\) 根号分治,先特判 \(b=1\)。当 \(b\le B\) 时暴力枚举 \(b\) 然后枚举 \(j\) 求解;当 \(b>B\) 时有 \(a_j\le \tfrac{V}{B}\),此时暴力枚举因数作为 \(b\),复杂度为 \(O\left(\sqrt{\tfrac{V}{B}}\right)\)。平衡一下取 \(B=V^{\tfrac13}\),复杂度 \(O(nV^{\tfrac{1}3})\)

P5071 [Ynoi2015] 此时此刻的光辉

我们知道约数个数的式子是 \(\prod (c_i+1)\),一种办法是直接枚举所有质数然后利用前缀和计算,复杂度为 \(O(nV)\);另一个想法是利用莫队,加入或删除一个数的时候可以轻易维护出 \(c_i\) 的变化以及对答案的影响,复杂度 \(O(n\sqrt {n}\log V)\)

考虑平衡一下两部分的复杂度。对于 \(\le 1000\)\(168\) 个质因子我们暴力枚举利用前缀和计算,而剩下的部分每个数最多只有两个质因子,我们暴力跑莫队的复杂度就降为 \(O(n\sqrt n)\),于是可以通过。

P5607 [Ynoi2013] 无力回天 NOI2017

首先上来想到对于集合大小进行根号分治,尝试后发现复杂度只能做到 \(O(m\sqrt m)\),无法通过。考虑对另一个东西进行阈值分治,我们对一个数字的出现次数进行分治。先做一步容斥转化成求交,然后分类讨论:

  • 当出现次数 \(cnt>B\) 时:

    这样的数总共只有 \(O(\frac{m}{B})\) 个,我们利用 bitset 维护每个集合内每个数的出现状况,查询的时候直接求交,复杂度 \(O(\frac{m^2}{Bw})\)

  • 当出现次数 \(cnt\le B\) 时:

    此时我们插入一个数的时候,最多会对 \(O(B)\) 个二元组产生 \(1\) 的贡献,这样暴力维护复杂度是 \(O(mB)\) 的。此时需要注意一下空间,容易发现虽然我们会有 \(O(mB)\) 个贡献,但是实际上我们要统计的只有 \(O(m)\) 个位置,用哈希表维护一下这些位置的答案即可,可以做到空间 \(O(m)\)

那么此时复杂度为 \(O(\frac{m^2}{Bw}+mB)\),取 \(B=\sqrt{\frac{m}{w}}\),复杂度为 \(O(m\sqrt{\frac{m}{w}})\),可以通过。不过此时 bitset 的空间会炸掉,用经典套路进行 bitset 分块可以把空间做到线性,时间复杂度不变,可以通过。

P5386 数字游戏

考虑在值域上跑莫队,那么我们的问题现在变成了如下形式:给定一个 01 序列,每次将一个位置的值进行修改,求出一个区间 \([l,r]\) 中所有 1 的连续段的 \(\frac{len(len+1)}{2}\) 之和。显然可以用线段树维护,复杂度 \(O(n\sqrt n\log n)\),过不去。

考虑莫队和线段树配合非常垃圾,我们考虑用序列分块配合莫队。每个块维护的信息和线段树上每个节点维护的信息一致,我们现在需要 \(O(1)\) 修改 \(O(\sqrt n)\) 查询。如果已知每个块的信息 \(O(\sqrt n)\) 合并信息是简单的,问题在于怎样 \(O(1)\) 修改。

尝试后发现将 0 改为 1 是容易的,但是改回去是比较困难的。于是考虑将莫队改为回滚莫队,然后我们就只有这个操作了。考虑这个操作怎么 \(O(1)\) 维护,我们对每个点维护 \(pre,nxt\) 表示它在块内向前 / 向后延伸到的最远点,当修改一个点的时候,我们考察左右两个点的 \(pre,nxt\),然后更新两个点的值,因为连续段内的点的值不重要。根据这个我们也可以简单维护出段内信息的贡献。

然后就做完了,复杂度 \(O(n\sqrt n)\)

P5608 [Ynoi2013] 文化课

先考虑没有修改怎么做。我们可以用线段树标记拼接,具体的,维护下面几个值:左端的极长乘法段长度及其运算结果,右端的极长乘法段长度及其运算结果,当前区间的运算结果,当前区间右侧的符号,当前区间的长度。可以拿到 5 分。

然后考虑没有操作 \(1\),此时我们还是维护上面的所有信息,然后分类讨论一下:

  • 当修改区间符号为 + 时:

    此时左右两端极长乘法段长度都是 \(1\),且计算结果就是左右两端的数字。而当前区间的运算结果就是区间和。所以我们需要额外维护区间两端的数字以及区间和。

  • 当修改区间符号为 * 时:

    此时左右两端极长乘法段长度就是区间总长,并且所有运算结果都是区间乘积,所以额外维护区间乘积即可。

维护这些信息即可,此时可以拿到 24 分。

然后考虑区间赋值怎么做,此时修改左右两端的计算结果是容易的,问题在于如何求出区间整体的运算结果。我们设 \(a_i\) 表示长度为 \(i\) 的极长乘法段长度,那么结果显然是 \(\sum a_ix^i\) 这个多项式。容易发现 \(a_i\times i\le len\),于是根号分治可以分析出这个多项式的项数不超过 \(O(\sqrt{len})\)

那么问题就简单了,我们在 pushdownpushup 的时候直接暴力 \(O(\sqrt{len})\) 维护这个多项式即可。用主定理分析一下复杂度可以知道这样做的总复杂度是 \(O(\sqrt n)\) 的。不过注意到我们多项式求值的时候有一个 \(x^i\) 要用快速幂,似乎还要带一个 \(\log\)。我们的方法是按照次数从小到大排序,每一次从上一个次数快速幂转移过来,可以证明这样做的复杂度不超过 \(O(\sqrt{len})\),因此复杂度不变。

综上我们的复杂度为 \(O(n\sqrt n)\),可以通过此题。代码比较困难,不过在现代评测机下已经没有明显的卡常问题了。

P11706 [KTSC2020 R1] 穿越

先考虑给定 \(A,B\) 怎么做,有一个简单的 dp:设 \(f_{i,j}\) 表示当前在第 \(i\) 列,第 \(j\) 行的最优方案。转移有两种:

  • 从上一列的对应行转移过来,需要看有没有墙,有的话需要加 \(B\)
  • 转移过来之后可以在这一列上瞬移,我们找出 \(f_i\) 中的最小值,让这个最小值加 \(A\) 去更新整列的 dp 值即可。

那么上面的操作总共有三个:区间加,区间最小值和区间 \(\text{chkmin}\)。很显然可以用线段树维护,复杂度 \(O(n\log n)\)

然后考虑优化。在不知道 \(A,B\) 的情况下我们可以将 \(f\) 的值写作若干个二元组 \((x,y)\),表示使用了 \(x\)\(A\)\(y\)\(B\) 可以走到点 \((i,j)\)。很显然 \((x,y)\) 在下凸包上才能是最优解,我们现在希望求出这个下凸包。

套用一下最小乘积模型的技巧,我们先求出 \(x\) 最小和 \(y\) 最小的点 \(A,B\),然后我们求出使得 \(\triangle ABC\) 最大的点 \(C\),此时 \(C\) 一定在凸包上。用叉积的式子拆一下可以知道我们要求 \((y_A-y_B)x_C+(x_B-x_A)y_C\) 的最小值,把 \((y_A-y_B,x_B-x_A)\) 代入 \((A,B)\) 就可以用上面的 dp 求出 \(C\) 的坐标了。显然复杂度是 \(O(凸包点数)\) 乘上 \(O(n\log n)\) 的。此题中凸包点值域显然在 \(O(n)\) 范围内,根据经典结论,凸包点数是 \(O(n^{\frac{2}{3}})\) 的,所以总复杂度为 \(O(n^{\frac 53} \log n)\) 的。

最后查询的时候在凸包上二分即可,总复杂度 \(O(n^{\frac 53}\log n+q\log n)\)

P7881 [Ynoi2006] rmpq

容易发现的是假如我们进行了 \(k\) 次操作,那么整个平面实际上会被划分为若干个操作序列完全相等的等价类,并且等价类数量是 \(O(k^2)\) 的。那么我们考虑操作分块,每 \(B\) 次操作分成一块,这样的话每个块的等价类个数是 \(O(B^2)\) 的。在查询的时候枚举前面的每一块,找出当前点在哪个等价类中,然后累乘答案即可。

考虑怎么求出等价类及对应的值,显然暴力求单次复杂度是 \(O(B^3)\) 的,那么我们的总复杂度为 \(O(\frac{n}{B}\times B^3+n\times \frac{n}{B})\),取 \(B=O(n^{\frac 13})\) 有复杂度 \(O(n^{\frac 53})\),不够优秀。

考虑分治,每次将修改分成两半,求出两半对应的等价类后进行归并合并,复杂度是 \(T(n)=2T(\frac n2)+O(n^2)\),也就是 \(O(B^2)\) 的。那么此时我们的复杂度就是 \(O(nB+\frac{n^2}{B})\) 的,取 \(B=O(\sqrt n)\) 有复杂度 \(O(n\sqrt n)\),由于常数较小所以可以卡在要求的 \(2\times 10^7\) 次乘法内。

最后一个问题是这个题是一个强制在线的题,直接操作分块不太好实现。考虑利用二进制分组的思想,每一次将队尾的两个长度相同的块的等价类合并起来,这样的话复杂度和直接分治的复杂度是一致的。

QOJ1851 Directed Acyclic Graph

rererererererecall。

很显然这个题不弱于 DAG 可达性,根据众所周知的经典套路,考虑操作分块。对每 \(B\) 个操作分一块,先来考虑块内的贡献怎么处理。我们枚举当前询问之前的所有修改,需要判断这个修改是否可达当前询问,我们保留所有询问点跑一遍 DAG 可达性即可。

然后考虑重构权值,我们要求这一部分的复杂度不超过 \(O(n)\)。先对每个点求出影响它的最后一个 \(1\) 操作的操作时间 \(tim_i\),可以用拓扑排序简单求出。然后拿出块内的所有 \(2\) 操作,此时对于一个点,我们要找的 \(2\) 操作应该是操作时间大于 \(tim_i\) 且权值最小的操作。

我们把所有 \(2\) 操作按照修改的权值排序,然后对每个点求出哪些 \(2\) 操作可达它。接下来我们对于每个时间预处理一个状态表示有哪些 \(2\) 操作的时间大于这个时间。此时对于每个点,将时间限制和可达性限制的状态 \(\text{And}\) 一下就得到了能影响这个点的所有 \(2\) 操作。由于我们已经排过序,所以求出这个状态的 \(\text{lowbit}\) 就是权值最小的操作。

我们取 \(B=w\) 即可用 ull 维护可达性的操作,复杂度为 \(O(\frac{(n+m)q}{w})\),可以通过。

CF1476G Minimum Difference

首先这个题不弱于带修区间数颜色,考虑带修莫队。我们维护每个数字的出现次数 \(cnt_i\) 和出现次数为 \(i\) 的数字个数 \(ccnt_i\)。考虑如果我们已知这两个数组怎么求解第一问,在 \(ccnt\) 上跑双指针即可,复杂度是 \(O(n)\) 的。

不过容易想到的是利用根号分治的经典结论,\(ccnt\) 上有值的位置最多只有 \(O(\sqrt n)\) 个,所以实际上双指针的复杂度是 \(O(\sqrt n)\) 的,用链表维护所有有值的位置即可。

然后由于我们跑莫队的时候 \(cnt\) 单次变化量为 \(1\),所以可以轻松维护出这个链表的变化。总复杂度 \(O(n^{\frac53}+n\sqrt n)\)

ABC369G As far as possible

长链剖分模板题。很显然我们要尽可能把点选在叶子上,那么我们要求 \(k\) 个叶子节点构成的最大连通块。那么这就是经典题目了,对整棵树长链剖分,对于 \(k\) 的答案就是前 \(k\) 条长链的长度之和。贪心即可,复杂度 \(O(n\log n)\)

P12462 [Ynoi2018] 星野爱久爱海

先考虑 \(q=1\) 怎么做,和上一个题类似,但是这次没有给定根。不过根据经典结论,我们的根一定是直径的某个端点。所以以直径端点为根,选前 \(k-1\) 条长链即可。

然后不难发现,对于两个点集 \([l,mid]\)\([mid+1,r]\),如果我们已知他们的最优答案对应的 \(k\) 个点,那么点集 \([l,r]\) 的最优答案一定也产生于这 \(2k\) 个点中。

所以有一个显然做法:维护一棵线段树,在每一个区间维护最优点集。合并时将儿子的 \(2k\) 个点取出建虚树,从虚树中选 \(k\) 个点即可。这里就可以使用上面所述的全局做法了。复杂度为 \(O((n+q)k\log n)\),无法通过。

考虑分块,我们每 \(k\) 个点分一块,然后块间的最优点集用 ST 表维护,查询时合并散块和整块信息即可。这样的话复杂度就是 \(O(k\times \frac{n}{k}\log n+qk)=O(n\log n+qk)\) 的,可以通过。实际实现的时候需要非常精细才能做到这个复杂度,否则可能会多 \(\log\),不过也足够通过此题。

老哥数据结构

CF522D Closest Equals

宝宝题。每个位置可以看作一个点 \((i,nxt_i)\),权值就是 \(nxt_i-i\)。那么查询相当于查矩阵最小值,离线扫描线即可。复杂度 \(O(n\log n)\)

P8337 [Ynoi2004] rsxc

首先考虑第二个条件的意思是什么,我们把这些数全部放进线性基,假设线性基大小为 \(k\),那么线性基能异或出来的结果有 \(2^k\) 种。所以这个区间必须包含所有的 \(2^k\) 个数,实际上就是要满足区间内数的种类数为 \(2^k\)。于是合法的条件有两个:

  • 区间中数字种类数为 \(2^k\)
  • 区间对应的线性基大小为 \(k\)

考虑枚举 \(k\),枚举右端点 \(i\),此时会发现对于每个右端点,我们可以求出一个区间 \([L_i,R_i]\),当左端点处于这个区间的时候可以同时满足上面两个条件。我们可以分别求出两个条件对应的左端点区间然后求交,不过实际上这两个区间有一些关系,实际上最后我们要求的区间 \((L_i,R_i]\) 满足:

  • \(L_i\)\(i\) 左侧第一个满足将 \([L_i,i]\) 放入线性基中大小为 \(k+1\) 的点。
  • \(R_i\)\(i\) 左侧第一个满足 \([R_i,i]\) 中有 \(2^k\) 种数字的点。

我们先看 \(L_i\) 怎么求,显然需要用到前缀线性基。我们取出线性基中的所有 \(pos\) 并排序,假设排序后的数组为 \(rnk\),那么 \(rnk_i\) 就是第一个线性基大小为 \(i\) 的点。不过如果我们直接排序的话复杂度会多一个 \(O(\log\log V)\),容易发现当我们插入一个数的时候 \(pos\) 的改变只会删除一个再加入一个,并且加入的这个 \(pos\) 一定是最大的,那么我们单次 \(O(\log V)\) 删除即可。这一部分的复杂度是 \(O(n\log V)\)

然后看 \(R_i\) 怎么求,这个就很简单了,维护每个点的前驱后继,然后用双指针跑一边即可,复杂度是 \(O(n)\) 的。不过这个我们先枚举 \(k\) 才能求,所以复杂度实际上是 \(O(n\log n)\) 的。综上我们预处理的复杂度是 \(O(n\log n+n\log V)\) 的。

然后考虑查询,依然先枚举 \(k\),我们要求的实际上是下面的式子:

\[\sum_{i=l}^r \max(R_i-\max(L_i,l-1),0) \]

容易观察的是 \(L_i,R_i\) 均有单调性,所以这个东西我们可以拆成三个部分求解:

  • \(R_i<l-1\)
  • \(L_i<l-1\le R_i\)
  • \(l-1\le L_i\)

二分找出分界点,每一部分的贡献容易前缀和求出,但是我们还要枚举 \(k\),所以复杂度还是炸了。考虑这个分界点实际上是可以预处理出来的,所以二分可以省去,复杂度为 \(O(q\log n)\)

综上复杂度为 \(O(n\log n+n\log V + q\log n)\),可以通过此题。如果想要通过卡常版需要一些优化。

P9991 [Ynoi2023] TEST_107

这个题和第一题没有什么区别。容易发现的是对于点 \(i\),如果 \([i,nxt_i]\subseteq [L,R]\),那么 \((i,nxt_i)\) 就是一个合法区间,套用第一题的做法即可。

不过有一些区别在于我们可以取一个前缀或后缀。依然离线扫描线,用 set 维护所有出现的数字的最后一次出现位置,设 \(p\)set 中第一个大于 \(L\) 的点,那么 \((p,R]\) 就是一个合法的后缀。对于前缀同理维护,复杂度 \(O(n\log n)\)

CF407E k-d-sequence

考虑将所有数对 \(d\) 取模,这样可以将模数相同的一段拿出来进行处理。令 \(r\) 为当前段模数,令 \(a_i\leftarrow \frac{a_i-r}{d}\),则一个区间 \([l,r]\) 合法当且仅当:

  • \([l,r]\) 中没有重复数字。
  • \(\max(l,r)-\min(l,r)+1\le r - l + 1 + k\),即 \(\max(l,r)-\min(l,r)-(r-l)\le k\)

这个条件的处理是经典的,线段树直接维护即可,复杂度 \(O(n\log n)\)

P7560 [JOISC2021] フードコート

考虑对每个点求出这个点上当前出队的总人数,设其为 \(cnt\)。那么对于这个点的一个查询 \(B\),我们只需要找到这个点历史上第 \(cnt+B\) 次插入的是哪个数字即可求出答案。

先考虑怎样维护 \(cnt\),出队人数直接维护比较困难,正难则反,维护当前队列内人数和插入的总人数。后者是好算的,前者需要实现区间加和区间 \(\text{chkmax}\),也是容易维护的。

然后考虑怎样求答案,显然要考虑二分。在线做的话复杂度不太优秀,考虑离线,将序列-时间坐标轴翻转,我们在序列上做扫描线。那么插入可以被差分成一次单点加和一次单点删除,查询的时候线段树二分即可。复杂度 \(O(n\log n)\)

P11398 众数

先转化问题,我们肯定要枚举 \(k\) 然后求答案,问题就在于怎样快速求出前缀的众数。

Subtask 1~9

我们可以每一次修改的时候暴力修改每个位置上的前缀信息,然后众数就可以直接 \(O(1)\) 维护出来了。复杂度为 \(O(nm+\sum k)\)

对于没有修改操作的点,预处理出众数后就不用动了,复杂度 \(O(n+\sum k)\)

Subtask 10~15

前三个包的 \(b_i\) 很小,我们要维护的就是前缀 \(1\) 和前缀 \(2\) 的个数,比较这两个的大小即可。如果用树状数组维护的话 \(\sum k\) 上要带一个 \(\log\),不能承受。注意到我们的 \(k\) 是从后往前枚举的,因此我们可以考虑每次动态从全局的信息中删除当前点的贡献,这样就可以 \(O(1)\) 维护当前 \(1,2\) 的个数。复杂度 \(O(n+m+\sum k)\)

对于 \(b_i\) 较大的情况,我们不得不使用线段树来删除贡献,同时还需要维护全局 \(\max\),复杂度是 \(O((n+m+\sum k)\log n)\)。可以通过 Subtask 13~15。

Subtask 16~19

此时我们只关心后 \(300\) 个点的信息,一种办法是沿用暴力的做法,不过单次只修改这 \(300\) 个点的信息,复杂度 \(O(300m+\sum k)\)。不过这对正解没有什么启发。

考虑众数这个信息是容易加入但不容易删除的,我们之前的所有枚举都基于从后往前枚举前缀,这必然涉及到删除操作。那么考虑倒过来,从 \(n-300\) 开始向 \(n\) 枚举,每次动态加入一个点并维护众数,最后暴力求出后 \(300\) 个点的权值然后求答案即可。而我们要维护的信息只有前 \(n-300\) 个点的信息,单次修改 \(O(1)\) 即可,复杂度不变。

Subtask 20~25

我们现在的做法基于从前往后枚举,如果每次从 \(1\) 开始枚举接受不了。考虑分块,从后往前枚举每个块,块内按照上面的方式从前往后枚举求答案。此时我们需要维护 \(O(\sqrt n)\) 个前缀的信息,单次修改 \(O(\sqrt n)\);查询的时候我们的复杂度应该是 \(O(k+\sqrt n)\) 的,所以总复杂度为 \(O((n+m)\sqrt n+\sum k)\)

这个做法还是不够优秀,很显然这个块长还是浪费的比较多。考虑倍增,我们把块长改为 \(2^0,2^1,2^2,\cdots\),这样的话我们要维护的只有 \(O(\log n)\) 个前缀信息,同时单次查询复杂度为 \(O(k+\log n)\),所以总复杂度为 \(O((n+m)\log n+\sum k)\),可以通过。

CF1814F Communication Towers

枚举频率 \(x\),则每条边可以看做有一个 \([l,r]\) 的出现时间。考虑线段树分治,我们需要在遍历到叶子结点的时候给 \(1\) 所在联通块打上一个标记,最后所属联通块有标记的点就是合法答案。

这里需要用到一个并查集打标记的小 trick,在并查集的根节点处维护 \(tag\),把 \(x\) 合并到 \(y\) 上的时候先令 \(tag_x\leftarrow tag_x-tag_y\),而在撤销分裂的时候令 \(tag_x\leftarrow tag_x+tag_y\) 即可,这样标记之间不会影响并且可以实现下放,复杂度不变。

P11111 [ROI2023] 生产计划

考虑到答案的上下界 \([L,R]\) 是容易求出的,也就是全部取下界和全部取上界求出的最大独立集。

构造的时候考虑动态调整,初始时令 \(a_i=l_i\),每次将某一个 \(a_i\) 加一,那么答案的增量不超过 \(1\)。那么我们考虑对每个数按顺序执行这个操作,也就是对于一个前缀的 \(a_i\) 令其为 \(r_i\),对于一个后缀的 \(a_i\) 令其为 \(l_i\),而中间的 \(a_i\) 正在从 \(l_i\) 调整到 \(r_i\)

于是令 \(v_i\) 表示 \(a_{1\sim i}\) 取到 \(r\)\(a_{i+1\sim n}\) 取到 \(l\) 时的最大独立集,查询的时候二分找到第一个 \(v_i>v\) 的点,则这个点就是正在调整的点。可以用全局平衡二叉树维护 ddp 做到 \(O((n+q)\log n)\)

不过这个顺序显然是我们自己钦定的,那我们考虑一个更简单的顺序。实际上按照 DFS 序进行修改即可,这样的话贡献可以轻易用换根 dp 求出,复杂度 \(O(n+q\log n)\)

CF1344E Train Tracks

我们考虑对每个点求出有哪些火车会在哪些时刻到达他,并且每个时刻他的站台应该指向哪个儿子。我们用 \((t,x)\) 来描述在 \(t\) 时刻会有一个火车进入这个点,并且要求站台指向 \(x\)。将二元组按照 \(t\) 排序,如果两个相邻二元组 \((t_1,x_1),(t_2,x_2)\) 满足 \(x_1,x_2\) 不同,那么在时间 \((t_1,t_2]\) 内必须要将这个点的站台转一次。

假如我们对每个点求出了所有的区间,那么问题转化为一个经典的贪心问题:有若干个项目,每个项目要求在时刻 \([l,r]\) 内完成,问有没有方案。对时间轴扫描线,每次加入右端点最小的项目即可。设区间个数为 \(k\),则复杂度为 \(O(k\log k)\)

现在的问题是求出这些区间的复杂度以及区间个数 \(k\) 的数量级。自顶向下考虑不太方便,我们让火车倒过来,自底向上进行合并。考虑启发式合并,每次继承重儿子的所有时刻,将轻儿子插入,每次插入最多新增 \(O(1)\) 个区间,那么根据启发式合并的结论,我们的区间总数应该是 \(k=O(n\log n)\) 数量级的。而我们要维护的操作是全局加,插入和查询前驱后继,用 set 直接实现即可。总复杂度 \(O(n\log^2 n)\),可以通过。

P10789 [NOI2024] 登山

先考虑一个 \(O(n^2)\) 的 dp,设 \(f_i\) 表示从 \(i\) 出发走到 \(1\) 的方案数。转移的时候我们先下滑到子树中的任意一个点,然后向上跳跃。可以发现每次跳跃之后先前的全部限制都会失效,所以这么转移是正确的。这里我们先钦定一些变量,令 \([l_i,r_i]\) 表示 \(i\) 能跳到的深度范围,\(lim_i=d_i-h_i-1\)\(w_{i,j}\) 表示 \(i\to j\) 链上 \(lim\) 的最小值。那么对于 \(f_i\),我们先枚举子树内的点 \(j\),则转移点 \(k\) 需要满足 \(d_k\in [l_j,\min(r_j,w_{i,j})]\)。前缀和优化即可做到 \(O(n^2)\)

然后考虑 \(l_i=r_i\) 的部分,此时对于一个 \(j\),我们能跳的 \(k\) 只可能有一个点了。我们考虑在求出 \(k\) 之后枚举所有能跳到 \(k\)\(j\),然后用 \(j\) 去更新所有合法的 \(f_i\)。容易发现 \(i\to j\to k\) 合法当且仅当 \(r_j\le w_{i,j}\),而这是 \(j\) 的根链的一段后缀,倍增求出后就是一个链加,可以做到 \(O(n\log n)\) 维护。

然后考虑 \(l_i\ne r_i\),我们继续沿用上面的思想,也就是通过 \(j\) 来更新 \(i\) 的值。将 \(j\) 的贡献拆成 \(s_{\min(r_j,w_{i,j})}-s_{l_j-1}\),考虑每一部分的贡献:

  • \(s_{l_j-1}\) 的贡献:

    这一部分的处理和 \(l_i=r_i\) 的处理是类似的,只有 \(l_j\le w_{i,j}\)\(i\) 才是合法的。

  • \(s_{\min(r_j,w_{i,j})}\) 的贡献:

    这里依然要拆成两部分来求解,分类讨论一下 \(\min\) 的取值:

    • \(r_j\le w_{i,j}\) 时,此时合法的 \(i\) 依然可以倍增求出后进行链加。

    • \(r_j>w_{i,j}\) 时,我们考虑 \(w_{i,j}\) 是哪个点的 \(lim\),我们记 \(c_{i,j}\) 表示 \({i\to j}\) 路径上最深的满足 \(lim_u=w_{i,j}\)\(u\)。那么我们枚举 \(u\),需要计算的就是有多少 \(i,j\) 满足 \(c_{i,j}=u\)

      显然此时 \(i,j\) 是独立的,先来考虑 \(j\)。此时条件是 \(l_j\le w_{u,j}< r_j\),并且 \(u\)\(j\) 根链上的一个后缀最小值。我们令 \(pa_i\) 表示 \(i\) 的根链上深度最深的满足 \(lim_j<lim_i\)\(j\),那么 \(pa\) 会构成一个新的树。而合法的 \(u\) 显然在 \(j\) 的根链上,倍增求出范围后我们在新的树上做树上差分即可求出合法的 \(j\) 的个数。

      最后考虑 \(u\) 能贡献给哪些 \(i\),容易发现 \((pa_u,u]\) 这条链上的所有 \(i\) 都是合法的,显然这还是链加,直接维护即可。

综上我们可以在 \(O(n\log n)\) 的复杂度内维护出 \(f\) 值,可以通过。

posted @ 2026-01-10 12:27  UKE_Automation  阅读(15)  评论(1)    收藏  举报