bzoj3732

转自hwzzyr的博客
kruskal重构树
借用网上找来的其他图片解释
由于重构树中把原树的点权转换成为了新建节点的边权,这一过程是这样实现的。
首先对边排序
然后使用并查集辅助加边,每新建一条边时:
新建节点indexindex(编号从n+1n+1开始)
将原有两节点所在集合改为indexindex
将原有节点与indexindex连边
新建节点的权值为当前边的边权
给一下简单的代码

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 ;
}

代码复杂度很低,时间复杂度是优秀的O(nlog2n)

例题bzoj3732

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e4+2;
int n,m,q,cnt;
int fa[N<<1],f[N<<1][20],dep[N<<1],val[N<<1],g[N<<1][2];
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
struct data{
	int u,v,w;
	bool operator<(const data &a)const{return w<a.w;}
}e[N<<1];
inline int ask(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	int t=dep[u]-dep[v];
	for(int i=0;i<20;i++)if(t&(1<<i))u=f[u][i];
	for(int i=19;~i;i--)if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
	if(u!=v)u=f[u][0];
	return u;
}
inline void dfs(int u){
	if(!g[u][0])return;
	dep[g[u][0]]=dep[g[u][1]]=dep[u]+1;
	dfs(g[u][0]),dfs(g[u][1]);
}
int main(){
	scanf("%d%d%d",&n,&m,&q),cnt=n;
	for(int i=0;i<m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	sort(e,e+m);
	for(int i=1;i<=n;i++)fa[i]=i,fa[i+n]=i+n;
	for(int i=0;i<m;i++){
		int u=e[i].u,v=e[i].v;
		if(getfa(u)==getfa(v))continue;
		g[++cnt][0]=fa[u],g[cnt][1]=fa[v];
		fa[fa[u]]=fa[fa[v]]=f[fa[u]][0]=f[fa[v]][0]=cnt;
		val[cnt]=e[i].w;
	}
	dfs(cnt);
	for(int j=1;j<20;j++)
		for(int i=1;i<=cnt;i++)f[i][j]=f[f[i][j-1]][j-1];
	for(int x,y,i=0;i<q;i++){
		scanf("%d%d",&x,&y);
		printf("%d
",val[ask(x,y)]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/MikuKnight/p/9865550.html