「学习笔记」Kruskal重构树
Description
Kruskal 求最值生成树时需要通过边合并两个之前不相连的连通块
这时候通过建立虚点表示两点之间有连边,同时将边的信息记录到虚点上
所以如果查询原图上的两点间路径上的极值,可以考虑维护重构树路径上点权的信息
代码大概长成这样子,变量名还是比较大众化的:
nodes=n;
for(int i=1;i<=m;++i){
int x=e[i].from,y=e[i].to; x=find(x); y=find(y);
//找到边上两点的连通块代表,两个连通块的信息在该处存储
if(x==y) continue;
int z=++nodes; fa[x]=fa[y]=fa[z]=z;
val[z]=e[i].w; Add_edge(z,x); Add_edge(z,y);
}
建出来的树显然是个二叉树,不过另外有一些性质:
\(1.\) 每个点到根经过路径上的点权是单调的,因为是 Kruskal
\(2.\) 树上除了叶子节点是对应原来图上的点之外,别的所有点是对应树上的边
例题
NOI2018 归程
首先发现这里限制了一个维度(海拔)
就是先按照海拔从大到小排个序,扔到重构树里面
这里的重构树是一个小根堆,如果走到小于海拔限制的就不能走了
每次查询,直接从重构树上面往上走,走到那个超过海拔限制的点结束
这部分需要树上倍增
然后该点的子树里面的所有点都是可以直接坐车到的
然后考虑的就是找到这些点里面到 \(1\) 距离的最小值
直接 \(dfs\) 解决掉就好了
IOI2018狼人
首先建重构树维护可以走到的点,建一个大根的,一个小根的
然后在以两个点为根的子树里面求交集
如果有交集,那么就是 \(1\),否则就是 \(0\)
树的子树求交集问题?
先求出来 \(dfs\) 序,然后以 \(d_A\) 为下标,\(d_B\) 为权值建主席树
然后查询的本质就是跳 \(father\) 和二维数点,使用你喜欢的数据结构维护即可
树上路径
在算法讲述的过程中提到了一个叫做点到根的路径上权值单调的性质
其实Kruskal重构树是树上 Cartisian树 的一种常见形式
先建出来两个大根小根重构树(原树上边权就是较大点点权),这里可以简洁地正序、倒序扫描
本题所求也就是说在两个树上祖孙关系相反的点对数,使用BIT求解即可