货车运输(ybt)(洛谷P1967)

使用算法:kruskal构造最大生成树(森林)+倍增LCA
前置知识:
kruskal模板
倍增求LCA

洛谷传送门
题意概述

一张n个点m条边的无向图,每条边边权为w,给出q个查询,查询x,y之间有无路径联通,若无路径,则输出-1,若有路径,则输出两点之间所有路径中,路径上边权最小值的最大值。

算法分析

(1)考虑贪心,很容易发现某些边是不可能被经过的,所以可以考虑删边,即构造最大生成森林。(由于不能保证所有点都属于同一个连通块,因此不是最大生成树,是多棵树)。
tips1:构造最大生成森林不能只加入n-1条边,应当加入所有端点不属于同一个树的边
(2)构造最大生成森林的正确性:(反证法)
假设一条边a被选作了(x,y)之间路径上的答案,而不属于最大生成森林,那么最大生成森林中一定存在一条路径连接(x,y),该路径不包括边a,且路径上边权最小的边即路径答案一定大于a。因此具有正确性。
(3)构造森林后,考虑用选择森林中任意一个点作为根节点,用倍增LCA选出路径,并得到路径答案。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+4,maxm=1e6+4,INF=1e9+7;
int n,m,fa[maxn],dep[maxn],w[maxn][30],head[maxm],anc[maxn][30];
int f[maxn],ecnt=-1;
bool vis[maxn];
struct mint
{
	int v,w,u;
}a[maxm];
struct lena
{
	int nxt,v,w,u;	
}e2[maxm];
bool cmp(mint a,mint b)
{
	return a.w>b.w; 
}
void addline(int u,int v,int w)
{
	e2[++ecnt].nxt=head[u];
	e2[ecnt].w=w;
	e2[ecnt].v=v;
	e2[ecnt].u=u;
	head[u]=ecnt;
	return;
}
int Find(int x)
{
	if(f[x]==x) return x;
	return f[x]=Find(f[x]);	
}
void kruskal()
{
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=n;++i) f[i]=i;
	for(int i=1;i<=m;++i)
	{
		if(Find(a[i].u) != Find(a[i].v))
		{
			f[Find(a[i].u)]=Find(a[i].v);
			addline(a[i].u,a[i].v,a[i].w);
			addline(a[i].v,a[i].u,a[i].w);
		}
	}
	return;
}//kruakal模板,将最大生成森林再建一张图
void dfs(int x)
{
	vis[x]=1;
	for(int i=head[x];~i;i=e2[i].nxt)
	{
		int v=e2[i].v;
		if(vis[v]) continue;//避免回到父节点
		dep[v]=dep[x]+1;
		anc[v][0]=x;
		w[v][0]=e2[i].w; 
		dfs(v);
	}
	return;
}//dfs完成LCA的预处理
int LCAans(int x,int y)
{	
	if(Find(x)!=Find(y)) return -1;
	int ans=INF;
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;--i) 
	{
		if(dep[anc[x][i]]>=dep[y])
		{
			ans=min(ans,w[x][i]);
			x=anc[x][i];
		}
	}
	if(x==y) return ans;
	for(int i=20;i>=0;--i)
	{
		if(anc[x][i]!=anc[y][i])
		{
			ans=min(ans,min(w[x][i],w[y][i]));
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	ans=min(ans,min(w[x][0],w[y][0]));
	return ans;
}//查找答案
int main()
{
	memset(head,-1,sizeof(head));
	int x,y,z;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		a[i].u=x;
		a[i].v=y;
		a[i].w=z;	
	}
	kruskal();
	for(int i=1;i<=n;++i)
	{
		if(!vis[i])
		{
			anc[i][0]=i;
			w[i][0]=INF;
			dep[i]=1;
			dfs(i);	
		}
	}
	for(int i=1;i<=20;++i)
	{	
		for(int j=1;j<=n;++j)
		{
			anc[j][i]=anc[anc[j][i-1]][i-1];
			w[j][i]=min(w[j][i-1],w[anc[j][i-1]][i-1]);	
		}
	}//倍增预处理LCA所需的w、anc数组
	int q;
	scanf("%d",&q);
	for(int i=1;i<=q;++i)
	{
		scanf("%d%d",&x,&y);
		int ans=LCAans(x,y);
		printf("%d\n",ans);
	}
	return 0;
}	
posted @ 2021-09-07 17:36  Mint-hexagram  阅读(122)  评论(0编辑  收藏  举报