BZOJ 3732 Network 【模板】kruskal重构树

【题解】

首先,我们可以发现,A到B的所有路径中,最长边的最小值一定在最小生成树上。我们用Kruskal最小生成树时,假设有两个点集U,V,若加入一条边w(u,v)使U,V联通,那么w就是U中每个点到V中每个点的路径上的最长边。因为我们每次在可选的w中选择了最小的,所以可以满足最长边最短的要求。

我们可以做kruskal,当A与B恰好连通时,当前加入的边w就是A中的每个点到B中的每个点的最长边。

但这种做法在本题中似乎不可行。。因为本题中询问有很多组,效率上有问题(或者是我太傻了QAQ

其实也可以先跑kruskal,再做LCA倍增法来求a到b的最长边

我的做法是kruskal重构树,方法是按照kruskal求最小生成树的方式加边,但每次在加边时,新建一个节点,然后把两个点集的代表元作为其左右儿子,把新建节点的点权设为当前边的边权。

然后我们可以发现这棵树具有一些神奇的性质:

1.这棵树是一棵二叉树;

2.这棵树的父亲的点权大于左右儿子的点权;

3.在原图上,任意两点之间的路径的最长边的最小值等于kruskal重构树上它们的Lca的点权。

这样,这道题就可以完美解决了。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,m,k,tot,x,y,fa[maxn],f[maxn][32],val[maxn],dep[maxn];
struct edge{int x,y,w;}e[maxn];
struct child{int l,r;}ch[maxn];
void read(int &k){
	k=0; int f=1; char c=getchar();
	while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
	while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar();
	k*=f;
} 
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}  
bool cmp(edge a,edge b){return a.w<b.w;}
void dfs(int u){if (ch[u].l) dep[ch[u].l]=dep[ch[u].r]=dep[u]+1,dfs(ch[u].l),dfs(ch[u].r);}
int lca(int x,int y){
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=0,t=dep[x]-dep[y];i<20;i++) if (t&(1<<i)) x=f[x][i];
	for (int i=19;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main(){
	read(n); read(m); read(k); tot=n;
	for (int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].w);
	sort(e+1,e+1+m,cmp);
	for (int i=1;i<=n;i++) fa[i]=i,fa[i+n]=i+n;
	for (int i=1;i<=m;i++) if (find(x=e[i].x)!=find(y=e[i].y)){
		ch[++tot].l=find(x); ch[tot].r=find(y);
		fa[find(x)]=fa[find(y)]=f[find(x)][0]=f[find(y)][0]=tot;
		val[tot]=e[i].w;
	}
	dfs(tot);
	for (int j=1;j<20;j++) for (int i=1;i<=tot;i++) f[i][j]=f[f[i][j-1]][j-1];
	for (int i=1;i<=k;i++) read(x),read(y),printf("%d
",val[lca(x,y)]);
	return 0;
}

  

原文地址:https://www.cnblogs.com/DriverLao/p/7778969.html