2026 省选做题记录 1 (1/26-2/1)
2026 省选做题记录 1(1/26~2/1)
\(\textcolor{purple}{\text{A. P4211 [LNOI2014] LCA}}\)
题目大意
给定一棵以 \(1\) 为根,\(n\) 个顶点的有根树。\(q\) 次询问,每次给定 \((l,r,z)\),记 \(dep_x\) 表示 \(x\) 到根路径上的节点数,求 \(\sum_{i=l}^{r} dep_{lca(i,z)}\)。
\(n,q\le 50000\)。
思路分析
先将询问差分,只需要求 \(\sum_{i=1}^{x} dep_{lca(i,z)}\)。将 \(dep_{lca(i,z)}\) 转化为:将 \(i\) 到根的路径上的点加 \(1\) 后 \(z\) 到根的路径上的点的和。于是可以对 \(x\) 这一维扫描线,每次将 \(x\) 到根路径加 \(1\),查询 \(z\) 到根路径上的点的和。用树剖线段树维护,复杂度 \(O((n+q)\log^2 n)\)。
\(\textcolor{0E1D69}{\text{B. CF757G Can Bash Save the Day?}}\)
题目大意
给定一棵以 \(1\) 为根,\(n\) 个顶点的带权有根树,以及一个 \(1\sim n\) 的排列 \(a_i\)。\(q\) 次操作,每次可以交换 \((a_x,a_{x+1})\),或者给定 \(l,r,v\),求 \(\sum_{i=l}^{r} dist(a_i,v)\)。强制在线。
\(n,q\le 200000\)。
思路分析
先差分询问。考虑把 \(dist(a_i,v)\) 拆成 \(dep_{a_i}+dep_v-2\times dep_{lca(a[i],v)}\)。前两部分容易维护。最后一部分考虑边转点后使用 P4211 的 trick,用主席树维护加入了 \(a_1\sim a_i\) 后的版本。每个版本的修改就是有 \(\log n\) 次区间加。对于交换操作发现只需要重构第 \(x\) 个版本的主席树即可。发现空间是双 $\log $ 的,爆了。首先考虑使用标记永久化。其次,每次计算一个新版本的时候,如果加入一个区间的时候访问了一个刚刚新建的节点,就不需要再新建浪费空间了,直接在这个节点上改就行。最后,设定一个节点个数的阈值 \(B\)。如果当前节点个数超过了 \(B\),那么就重构主席树。加上这三个优化后就可以通过。
\(\textcolor{purple}{\text{C. P9665 [ICPC 2021 Macao R] Colorful Tree}}\)
题目大意
维护一棵以 \(1\) 为根,每个结点有颜色 \(c_i\),每条边带边权的有根树,初始只有根节点。支持 \(q\) 次操作。每次操作可能是动态加叶子或者改变一个节点的颜色。每次操作后,你需要找到两个节点 \(u\neq v\),满足 \(c_u\neq c_v\) 且 \(dist(u,v)\) 最大。输出这个距离。如果不存在输出 0。
\(q\le 500000\)。
思路分析
由于没有强制在线,可以先建出树的形态,加叶子可以看作激活节点。我们发现对于同种颜色的节点,只有其直径是有用的。我们可以通过对每种颜色建出线段树做到动态获得每种颜色节点的直径。接下来对颜色这一维建线段树,维护节点内的直径和异色直径。异色直径可以由两个子树内的异色直径和两边同色直径内的点转移而来。只需要做到 \(O(n\log n)-O(1)\) LCA,所有操作都是单 $\log $ 的。总复杂度 \(O((n+q)\log n)\),有一定的常数。
\(\textcolor{purple}{\text{D. AGC017D Game on Tree}}\)
题目大意
有一棵以 \(1\) 为根,有 \(n\) 个结点的树。Alice 和 Bob 轮流在树上操作,Alice 先。每次一方可以选择一条边并断掉它,将树分成两个连通块,并删掉不包含根的连通块。无法操作的人输。求谁将获胜。
\(n\le 100000\)。
思路分析
首先考虑计算有 \(n\) 个结点的链的 sg 值。发现这其实就是大小为 \(n-1\) 的取石子游戏,不过在边上进行,因此为 \(n-1\)。
接下来考虑如果知道了 \(u\) 的儿子 \(v_1\sim v_m\) 的 sg 值如何算出 \(sg_u\)。可以将 \(v_i\) 的子树形态等价为有 \(sg_{v_i}+1\) 个节点的链。于是发现这其实就是一个有 \(m\) 堆石子,每一堆有 \(sg_{v_i}+1\) 个石子的取石子游戏,\(sg\) 值就是 \(\oplus_{i=1}^{m} (sg_{v_i}+1)\)。\(O(n)\) 计算即可。
\(\textcolor{purple}{\text{E. CF932F Escape Through Leaf}}\)
题目大意
有一棵以 \(1\) 为根,\(n\) 个节点的树。每个节点有 \((a_i,b_i)\)。对于 \(x=1\sim n\),分别求解以下问题,并输出答案:
你当前在节点 \(x\),想要到达一个叶子。每次你可以以 \(a_x\times b_y\) 的代价跳跃到你子树中的一个节点 \(y\)(不包括自身)。求最小需要的代价以到达一个叶子。
\(n\le 100000, -100000\le a_i,b_i\le 100000\)。
思路分析
设 \(dp_x\) 表示当前在 \(x\) 跳到叶子的代价。转移就是枚举子树内的一个点。发现这个转移其实就是,你有一个直线的集合(大小为 \(sz_x-1\)),然后需要查询 \(x=a\) 时候的最小值。考虑 dsu on tree,用动态开点李超树维护这个集合。从重儿子那里继承这个集合,并暴力把轻儿子子树里的直线塞进去。如果 \(keep=0\) 则清空李超树。时间复杂度 \(O(n\log n\log V)\)。另一种做法是李超树合并。观察到一条直线只会下放 \(\log V\) 次,因此直接合并即可。
\(\textcolor{purple}{\text{F. CF1119F Niyaz and Small Degrees}}\)
题目大意
有一棵 \(n\) 个结点带边权的树。对于 \(x=0\sim n-1\),分别求解以下问题,并输出答案:
最少需要删掉权值总和为多少的边,才能够让每个点的度数都 \(\le x\)?
\(n\le 250000\)。
思路分析
如果一个点的度数 \(\lt x\),我们不会主动去删它相邻的边,除非度数 \(\ge x\) 的点需要。因此对度数 \(\ge x\) 的点建虚树,容易发现虚树总大小是 \(O(n\log n)\) 的。在虚树上面 dp,设 \(f_{x,0/1}\) 表示是否强制删除/保留 \((x,fa_x)\) 这条边。发现需要对每个点维护一个数据结构,支持:删除一个数,插入一个数,查询前 \(k\) 小且 \(\lt 0\) 的数的和。观察到 \(k\) 是单调的,因此用两个堆维护一下就行了。
\(\textcolor{grey}{\text{G. QOJ9676 Ancestors}}\)
题目大意
有一棵 \(n\) 个结点的树,以 \(1\) 为根。\(m\) 次询问,每次给定 \(l,r,x\),求有多少个节点满足它是 \(l\sim r\) 中某个点的 \(x\) 级祖先。
\(n\le 100000\), \(m\le 1000000\)。
思路分析
考虑固定 \(x\),就是区间数颜色问题。现在的目标是,我们对每个点求出 \(pre_i\) 表示最大的 \(\lt i\) 的节点满足这两个点的 \(x\) 级祖先相等。同时,随着我们扫描 \(x\),我们希望能够快速更新 \(pre_i\) 应对查询。
我们发现可能互相成为 \(pre\) 的点深度一定一样。随着 \(x\) 的增加,这些点之间会逐渐连边,直到最后连通。对于每种深度,将这些点按照 bfs 序排列起来,我们就能快速知道两个点合并的时间。只需要支持:每次合并两个集合,并且同时维护它们的 \(pre\)。考虑我们在一个集合中加入一个数,\(pre\) 的改变是 \(O(1)\) 的。使用启发式合并,容易得到最后 \(pre\) 的改变是 \(O(n\log n)\) 的。这是一个很好的结果,我们可以预处理出每个 \(x\) 处,哪些 \(pre\) 会发生变化。
差分询问后,现在的问题其实就是,有 \(n\log n\) 个三维点,每个点有一个权值 \(w_i=1\) 或 \(-1\)。\(m\) 次询问,每次给定 \((a_i,b_i,c_i)\),求第一维 \(\le a_i\),第二维 \(\le b_i\),第三维 \(\le c_i\) 的点的权值和。容易用 cdq 分治做到双 \(\log\)。注意:我们不能承受每次分治的时候对第二维重新排序,归并即可。
\(\textcolor{0E1D69}{\text{H. P12504 [ROI 2025 Day1]}}\) 树上的青蛙
题目大意
有一棵有 \(n\) 个节点的树。每个节点上有一只青蛙。你需要找到一组青蛙之间的最大匹配,使得每对青蛙之间的距离是奇数,且都 \(\le d\)。
\(n,d\le 500000\)。
思路分析
发现显然 \((u,v)\) 的深度奇偶性不同。我们考虑将 \((u,v)\) 在 \(lca(u,v)\) 及其祖先 \(x\) 处匹配,使得 \(dep_u+dep_v-2\times dep_x=d\)。容易发现这不劣;如果我们早早的匹配掉了,那么之后就没法反悔了。在能匹配掉的最晚处匹配可以保证我们随时可以反悔。贪心策略很明显了:我们进行一遍 dfs,维护子树内当前深度为奇数/偶数的未匹配的点。每次在一个点,我们考察所有可能的深度 \((u0,v0)\),形成 \(\min(cnt_{u0},cnt_{v0})\) 对匹配。容易发现具体匹配的点不重要。最后到了根匹配完,剩下来的点就不用再强求为 \(d\) 了,只要距离 \(\le d\) 的都可以。每次找到一个集合中深度最大的点,尝试和另一个集合里满足条件的深度最大的点匹配。总复杂度 \(O(nd)\)。
如何优化?我们发现复杂度瓶颈在于我们每次都带着一堆配不到的点往上跑,每次都得重新扫一遍。考虑对于每个点维护如下几个值:它的深度;两个 \(\text{std::map<int,std::vector<int>>}\) ,分别维护深度为奇数/偶数的点,key 是深度,\(\text{std::vector<int>}\) 存储了待匹配的这个深度的所有点;一个 \(\text{std::priority_queue}\) (大根堆),维护三元组 \((exp,u0,v0)\),表示期望在 \(exp\) 深度匹配一个深度为 \(u0\) 的点和一个 \(v0\) 的点。在贪心的时候,我们首先使用启发式合并,从重儿子那里继承维护的几个值,并将深度减 \(1\),map 里面深度过高的点全部删掉;接下来暴力枚举每个轻儿子里还没有匹配的点,加到这个数据结构里。我们实现一个函数 \(\operatorname{augment}(x)\),表示尝试给深度为 \(x\) 的点预设一个匹配。从奇偶性相反的 map 里找到一个点,满足匹配的位置在 \(x\) 及 \(x\) 的祖先而且深度尽量大,将这组匹配 push 到 priority_queue 当中。每次加入一个点的时候,如果这是这个深度的点第一次加入数据结构,就调用 \(\text{augment}(x)\)。
全都加入数据结构后,就可以开始匹配了。我们对每个深度都已经预设了匹配,因此我们不用担心遍历的浪费。我们依次 pop 掉大根堆中的三元组。如果期望匹配的深度 \(\lt lim\) 就跳出;否则我们看一下当前有没有深度为 \((u0,v0)\) 的两个点。如果没有就 continue;否则随便在 map 中查找两个深度满足要求的点,匹配它们,并 pop_back。如果成功匹配了,就要重新给深度 \(\le u0\) 和 \(\le v0\) 的最大的两个深度调用 augment 函数,保证每个深度总有预期的匹配。最后把没匹配到的节点拉出来贪心。总复杂度 \(O(n\log^2 n)\)。
\(\textcolor{0E1D69}{\text{I. P10107 [GDKOI2023]}}\) 树
题目大意
给定一棵以 \(1\) 为根,有 \(n\) 个节点的有根树。每个节点有一个权值 \(v_i\)。\(q\) 次询问,每次给定 \(x,k\)。设 \(c_1\sim c_m\) 是 \(x\) 子树里满足和 \(x\) 距离 \(\le k\) 的所有节点。求 \(\sum_{i=1}^{m} v_{c_i}\oplus dist(c_i,x)\)。
\(n,q\le 1000000\)。
思路分析
同样的 trick:CF1511G。
放到 bfs 序 \(seq_i\) 上考虑。显然 \(x\) 在第 \(dep_x+1\) 层。我们记和 \(x\) 在一层的节点的 bfs 序编号为 \([l_i,r_i]\),同时记考虑和 \(seq_x\) 在同一层的所有 bfs 序编号 \(\le x\) 的结点,距离为 \(k\) 的答案是 \(f(x,k)\),答案就是 \(f(id_x,k)-f(id_x-1,k)\)。现在我们只需要考虑如何快速计算 \(f(x,k)\)。
考虑倍增:\(val(x,k)\) 表示考虑编号 \(\le x\) 节点子树内 \(2^k\) 层的答案(包括自身所在层)。如何合并?我们再预处理出 \(son(x,k)\) 表示 \(x\) 的 \(2^k\) 级儿子 bfs 序最大是多少。只需要考虑 \(val(son(x,k-1),k-1)\) 因为深度统一加了 \(2^{k-1}\) 所造成的额外贡献。这是容易的,我们只需要计算这个区域内 \(v_i\) 的第 \(k-1\) 位总共有多少个 1 即可。再预处理一个 \(count(x,k)\) 表示 \(x\) 左边及下面所有节点总共有多少个数第 \(k\) 位是 1,就可以 \(O(1)\) 计算了。查询也是容易的,从高往低倍增跳。总复杂度 \(O((n+q)\log n)\)。
\(\textcolor{0E1D69}{\text{J. P10789 [NOI2024]}}\) 登山
题目大意
给定一棵以 \(1\) 为根,有 \(n\) 个节点的有根树。每个节点 \(2\le i\le n\) 有以下信息:\((l_i,r_i,h_i)\)。记第 \(i\) 个节点的深度为 \(d_i\)。对于 \(i=2\sim n\),分别求出以下问题的答案:
有多少种满足条件的路径 \(v_1=i, v_2, \ldots, v_m=1\) 满足:\(fa_{v_{i+1}}=v_i\) 或 \(v_{i+1}\) 是 \(v_i\) 的 \([l_{v_i},r_{v_i}]\) 级祖先;如果 \(v_{i+1}\) 是 \(v_i\) 的 \([l_{v_i},r_{v_i}]\) 级祖先,那么对于任意 \(j=1\sim i\),都有 \(d_{v_{i+1}}\lt d_{v_j}-h_{v_j}\)。对 998244353 取模。
\(1\le T\le 4\) 组数据,\(n\le 100000\)。
思路分析
我们首先注意到,设我们从 \(i\) 一路滑落到 \(j\) 再跳跃到 \(k\),那么对于 \(k\) 深度的限制实际上只会来自于 \(i\sim j\)。之前的限制已经限制了 \(i\) 的深度,但是现在 \(i\) 比 \(k\) 还深,说明限制已经无效了。设计 \(dp_i\) 表示对于 \(i\) 问题的答案。我们先将 \(l_i,r_i\) 转为 \(v_i\) 跳跃的可能深度区间,\(h_i\) 转为 \(d_i-h_i-1\)。这样我们的转移就是:枚举 \(j\),则 \(k\) 的深度只会是 \([l_j,\min(r_j,lim_{i\to j})]\)。\(lim_{i\to j}\) 表示 \(i\) 到 \(j\),\(h_k\) 的 \(\min\)。我们在 dfs 的时候维护 dp 数组到根的前缀和 \(s_i\),就可以做到 \(O(n^2)\)。
贡献的形式是,在 \(\lim_{i\to j}\ge l_j\) 有贡献,贡献是 \(s_{\min(r_j,lim_{i\to j})}-s_{l_j-1}\)。把这个贡献拆开来考虑。先考虑第二项。对于一个 \(j\),我们考虑它的深度为 \(l_j-1\) 的祖先的 \(s_k\) 求出来后,会对哪些 \(i\) 有贡献。这些 \(i\) 要满足是 \(j\) 的祖先,且 \(lim_{i\to j}\ge l_j\)。这其实是 \(j\) 的一段祖先后缀链,倍增跳出这段链进行链加即可。再考虑第一项。先考虑 \(r_j\le lim_{i\to j}\) 的情况。我们考虑对于一个 \(j\),它的深度为 \(r_j\) 的祖先的 \(s_k\) 求出来后,会对哪些 \(i\) 有贡献。这个和前面没有区别,仍然是一段祖先后缀链,倍增跳出来链加。最后考虑 \(l_j\le lim_{i\to j}\lt r_j\) 的情况。这个 \(lim\) 一定是某个 \(h_x\)。对于一个 \(x\),我们考虑它的深度为 \(h_x\) 的祖先的 \(s_k\) 求出来后,会对哪些 \(i\) 有贡献。首先 \(i\) 一定是 \(x\) 的祖先,而且由于 \(h_x\) 就等于 \(lim_{i\to j}\),因此 \(lim_{i\to x}=h_x\)。这是 \(x\) 到根的一段祖先后缀链,跳出来链加。加的系数是满足条件的 \(j\) 的个数。这个 \(j\) 要满足 \(l_j\le lim_{x\to j}=h_x\lt r_j\)。我们把 \(x\) 往它最近的满足 \(lim_{x\to j}\neq h_x\) 的 \(j\) 连边,形成一颗树。那么满足条件的 \(j\) 在新树上 \(x\) 的子树里,只需要满足 \(l_j\le h_x\lt r_j\)。dsu on tree 用树状数组维护即可。而前面的链加,单点查也可以用树状数组维护。总复杂度 \(O(n\log^2 n)\).
浙公网安备 33010602011771号