【基础操作】树分治详解(点/边分治)
时隔 $???$ 年,我又回来探望树分治了……
本文于 2019.5.22 更新,删除了之前写的内容,大家如果要学习这个东西可以去看 IOI2019 论文(虽然那里好像也没有具体证明之类的)。
点分治套路
1. 连通块
第 K 大连通块
$n$ 个点的树,每个点有一个整数点权,连通块的权值为点权和,求权值非严格第 $k$ 大的连通块。
子任务1:$n\le 20$
子任务2:$n,k\le 1000$
子任务3:树是一条链,且第 $i$ 条边连接编号为 $i$ 和 $i+1$ 的点
子任务4:$n,k\le 10^5$
题解
子任务 $1$
连通块数量显然不超过点集数量,而点集共有 $2^n-1$ 个(去掉空集)。枚举选取的点集并判断其是否为连通块即可。
时间复杂度 $O(2^n\times n)$。
子任务 $3$
问题转化为:给你一个长度为 $n$ 的序列,求第 $k$ 大子序列。
用【十二省联考2019 异或粽子】那道题的做法就行,只需要把记录异或和改成记录加法和,线段树即可。
子任务 $2、4$
发现给的是树,一般与树有关的奇怪题目都跟点分治有关。
考虑点分治,不难发现子问题增加了 连通块必须包含重心。
这样就可以证明,对于树中任意一个连通块,该连通块中至少存在一个点,使得该点为重心的点分树 完全包含这个连通块。具体证明只需要考虑一下 重心是连接两棵点分树的边的一端点 这一性质即可,这里不赘述。
对于一棵点分树,以重心为根(即这个点的 $dfs$ 序为 $1$),重新给点分树中的点标 $dfs$ 序。然后我们考虑按 $dfs$ 序决策选择哪些点。要求选到的点必须是一个连通块。
那么走到 $dfs$ 序为 $i$ 的点时,决策很显然:
1. 选这个点,继续决策是否选其子树,即花费 $v_i$ 的代价到 $i+1$($v_i$ 为点权);
2. 不选这个点,那么这棵子树中的所有点都不能选,即花费 $0$ 的代价到 $i$ 的后戳 $+1$($i$ 的后戳就是 $i$ 号点的出栈时间)。
可以去论文原题看看画出来是什么样的。
于是一个连通块就对应唯一一条路径了。
现在就变成了 $O(n\log{n})$ 个点,$O(n\log{n})$ 条边的 $DAG$ ,求 $k$ 短路的经典问题。
在给定起点和终点的情况下,k 短路的模板:SDOI2010 魔法猪学院
这里讲一下具体怎么做:
预处理出以终点为根的最短路树 $T$,即求出每个点到终点的最短路长度。
定义一种边集 $E$,存一条从起点到终点的路径上,依次经过哪些非树边。
开一个小根堆,存若干个边集。
初始时小根堆里只有一个空集。
然后查询 $k-1$ 次小根堆的顶(注意只是查询,不弹出顶!)。
每次查询顶后,设顶边集 $E'$ 尾部的边为 $x$(尾部指边集 $E'$ 上次加入的一条边),有 $2$ 种方法得到一个新边集放入堆:
1. 将 $x$ 替换为起点相同的下一小非树边
2. 在 $x$ 的后面接上一条非树边,这条非树边的起点在最短路树 $T$ 中是 $x$ 的终点的祖先或同一点,且这条边是当前顶边集未接过的最大的边(也就是下一大)。
重复操作 $k-1$ 次后,堆顶就是第 $k$ 短路的边集了。
这种涵盖全局的维护方式跟【十二省联考2019 异或粽子】那道题的维护方式很相似,大家可以感性思考一下为什么这样是对的(建议先思考一个 $lemma$ 为什么是对的:下一短路径一定只需要在之前某一短路径上撇出去一条非树边)。
第 $1$ 种方法很好搞,对于每个点,开个 $vector$ 存非树边 并按边权排序即可。
第 $2$ 种方法就需要给每个点建一个 包含自己所有祖先连出去的非树边的堆。
把堆可持久化即可,每个点继承其父亲的堆,然后再与自己的堆合并,左偏树合并即可。
时间复杂度大概是 $O(k\log{n})$。
边分治套路
1. 多棵树套起来
两棵树
给出两棵点数均为 $n$ 的树,求两个点,最大化他们在两棵树上的距离和。
子任务1:$n\le 5000$
子任务2:对于两棵树,各自的第 $i$ 条边连接编号为 $i$ 和 $i+1$ 的点
子任务3:对于第一棵树,第 $i$ 条边连接编号为 $i$ 和 $i+1$ 的点;对于第二棵树,树是一条链
子任务4:对于第一棵树,第 $i$ 条边连接编号为 $i$ 和 $i+1$ 的点
子任务5:$n\le 100000$,边权可以为负
题解
【WC 2018】通道