食用须知:
*因为临时改动,代码不一定完全正确。
*以下概念来自个人理解,并不十分准确。慎!
<Kruskal重构树思想与模板>
Kruskal算法(维护无向图的最小生成森林):每一时刻,从没有选择过的边中选择一条权值最小的且这条边所连接的两个端点不在同一棵树上(不连通),就把该边加上,该边的两个点即为连通。连通情况可以用并查集维护。
Kruskal重构树基于 Kruskal 算法。已知在 Kruskal 的算法中,是按照边权的大小将原图中的边逐一加入到生成森林里,构成最小生成树。而 Kruskal 重构树在原图的 N 个节点的基础上,把 N 个原有节点作为叶子节点。将边权按大小关系,逐一插入到生成森林里。每枚举到一条边,若该边所连接的两个节点不在同一棵树上,那么就增加一个节点,该点的点权即为该边的边权,该点的左孩子和右孩子为该边所连接的两个节点原来所属生成树的根节点(初始根节点都为本身)。这样,就建成了一棵含 2N-1 个节点且所有非叶子节点都有权值的生成树。
举个栗子:
5个点8条边的一张图:
按时边权从小到大的排序为:
#节点编号 #节点编号 #权值
2 5 2
3 4 3
1 2 4
3 5 6
1 3 7
4 5 8
1 5 9
加入第一条边(E[i].u=2,E[i].v=5,E[i].d=2):
加入第二条边(E[i].u=3,E[i].v=4,E[i].d=3):
加入第三条边(E[i].u=1,E[i].v=2,E[i].d=4):
加入第四条边(E[i].u=3,E[i].v=5,E[i].d=6):
此时,原图中所有的点都集中在一棵生成树中。加入第五~八条边,并不影响原图,不再列出,代码实现如下:
void kruskal(){
for(int i=1;i<=M;i++){
int X=getfa(E[i].u);
int Y=getfa(E[i].v);
if(X==Y) continue;
fa[X]=fa[Y]=++N;//新增节点
pnt[N]=E[i].d;//将边权转移成点权
tr[N].l=E[i].u;
tr[N].r=E[i].v;//记录左右孩子的节点编号
}
}
Kruskal 重构树可以实现对于求点 u 到 v 的最大边权的最小值(或最小边权最大)的查询,答案即为在 Kruskal 重构树上,lca(u,v) 的点权。如上图中(上图将边权从小到大排序,解决的是两点之间最大边权的最小值), 点 2 到 点 5 的最大边权的最小值即为点 6 的点权:2 ;点 1 到点 4 的最大边权的最小值即为点 7 的点权:3。根据这个性质