Kruskal重构树

Kruskal重构树是基于Kruskal算法的,典型的应用是求两点之间边权最大值的最小值。


首先,我们运行Kruskal算法,将边从小到大排列,在运行过程中,每加入一条边,就建立一个节点u,权值为边权的权值w,然后所连两点a,b所在子树的根节点作为它的儿子,就像这样:


由于边是按升序加入的,所以这棵二叉树为大根堆,如果需要求两点之间在生成树中路径上最大权值,那么求两点LCA就好了



BZOJ3732

板题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
using namespace std;
const int maxn=30005,maxv=100005,maxm=100005,INF=2000000000,P=1000000007;

inline int read(){
	int out=0,flag=1;char c=getchar();
	while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}
	while(c>=48&&c<=57){out=out*10+c-48;c=getchar();}
	return out*flag;
}

int N,M,K;

struct EE{
	int a,b,w;
}Edge[maxm];

inline bool operator <(const EE& a,const EE& b){
	return a.w<b.w;
}

int head[maxv],nedge = 0;
struct EDGE{
	int to,next;
}edge[maxm];

inline void build(int a,int b){
	edge[nedge] = (EDGE){b,head[a]};
	head[a] = nedge++;
	edge[nedge] = (EDGE){a,head[b]};
	head[b] = nedge++;
}

void init(){
	fill(head,head+maxv,-1);
	N = read();
	M = read();
	K = read();
	for(int i=1; i<=M; i++){
		Edge[i].a = read();
		Edge[i].b = read();
		Edge[i].w = read();
	}
}

int nodei=0,pre[maxv],v[maxn];

inline int find(int x){return x == pre[x] ? x : pre[x]=find(pre[x]);}

void kruskal(){
	sort(Edge+1,Edge+1+M);
	for (int i = 1; i <= N; i++) pre[i]=i;
	nodei=N;
	int fa,fb;
	for (int i = 1; i <= M; i++){
		fa = find(Edge[i].a);
		fb = find(Edge[i].b);
		if (fa != fb){
			v[++nodei] = Edge[i].w;
			pre[fa] = pre[fb] = pre[nodei] = nodei;
			build(fa,nodei);
			build(fb,nodei);
		}
	}
}

bool vis[maxv];
int top[maxv],son[maxv],fa[maxv],siz[maxv],dep[maxv];

void dfs1(int u,int f,int d){
	fa[u] = f;dep[u] = ++d;siz[u] = 1;vis[u] = true;
	int to;
	for (int k = head[u]; k != -1; k = edge[k].next)
		if ((to = edge[k].to) != f){
			dfs1(to,u,d);
			siz[u] += siz[to];
			if (!son[u]||siz[to] > siz[son[u]]) son[u] = to;
		}
}

void dfs2(int u,int flag){
	top[u] = flag ? top[fa[u]] : u;
	int to;
	if (son[u]) dfs2(son[u],true);
	for (int k = head[u]; k != -1; k = edge[k].next)
		if ((to = edge[k].to) != son[u]&&to != fa[u])
			dfs2(to,false);
}

void division(){
	for (int i = 1; i <= nodei; i++)
		if (!vis[i]){
			dfs1(find(i),0,0);
			dfs2(find(i),0);
		}
}

int ask(int u,int v){
	while (top[u] != top[v])
		dep[top[u]] > dep[top[v]] ? u = fa[top[u]] : v = fa[top[v]];
	return dep[u] > dep[v] ? v:u;
}

void solve(){
	while (K--) printf("%d\n",v[ask(read(),read())]);
}

int main(){
	init();
	kruskal();
	division();
	solve();
	return 0;
}

NOIP2013货车运输也是一道这样的题

转载于:https://www.cnblogs.com/Mychael/p/8282874.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值