货车运输(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;
}