Kruskal重构树
前几天做noi2018的同步赛时,Day1T1我就拿了离线+树剖的80分暴力。
后来就知道有Kruskal重构树这样一种新科技(14年以前就有了)
什么是Kruskal重构树
简单来讲,就是在Kruskal算法进行的过程中,我们把最小生成树的边权改为点权。
这样,原树的节点个数变成2n-1个,并且有着许多有趣的性质。
Kruskal重构树的性质
1.根据我们构造的过程,这是一个二叉堆(后面再讲构造)
2.原树两点之间的边权最大值是重构树上两点Lca的权值
3.重构树中代表原树中的点的节点全是叶子节点,其余节点都代表了一条边的边权。
有了这些个性质,我们就可以解决一些问题了。
Kruskal重构树的构造
相信大家都会Kruskal
由于重构树中把原树的点权转换成为了新建节点的边权,这一过程是这样实现的。
首先对边排序
然后使用并查集辅助加边,每新建一条边时:
新建节点 index i n d e x (编号从 n+1 n + 1 开始)
将原有两节点所在集合改为 index i n d e x
将原有节点与 index i n d e x 连边
新建节点的权值为当前边的边权
给一下简单的代码
void Ex_Kruskal() {
int ind=n,lim=n<<1; sort(e+1,e+1+m);
for(int i=1;i<=lim;++i) f[i]=i;
for(int i=1;i<=m;++i) {
int fx=getfa(e[i].a),fy=getfa(e[i].b);
if(fx!=fy) {
f[fx]=f[fy]=++ind;
val[ind]=e[i].w;
add(ind,fx); add(ind,fy);
if(ind==lim-1) break;
}
} return ;
}
代码复杂度很低,时间复杂度是优秀的