正题
题目链接:
https://www.luogu.org/problemnew/show/P1967
大意
一个无向图,每个边有个权值,若干个询问,求两个点之间的一条最短路是这条最短路上的最小权值最大。
解题思路
首先我们发现其实每两个点之间留一条路径就好了。
然后我们会发现如果x到y的路上最小权值最大是w,那么如果z有一条边连向x,那么z到y的路上最小权值就有可能是w。
其实我们可以去掉一些边只留需要的最大的边就好了,这么一看其实就是留下最大生成树。那么在树上进行求LCA,然后在之前类似RMQ一样预处理一下就可以求两个点之间的最小权值了。
代码
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;
queue<int> dl;
struct line{int to,next,w,from;
};//边
struct pic{line a[100001];int ls[10001],tot;void addl(int x,int y,int w){a[++tot].to=y;a[tot].from=x;a[tot].next=ls[x];a[tot].w=w;ls[x]=tot;}
}p,tree;//图
int t,n,m,q,x,y,z,s;
int father[10001],f[10001][31],dis[10001][31],dep[10001];
int find(int x)//并查集-kruskal
{return father[x]==x?x:father[x]=find(father[x]);}
bool cmp(line x,line y)//排序-kruskal
{return x.w>y.w;}
void bfs(int open)//广搜预处理-树上倍增
{dl.push(open);dep[open]=1;while (dl.size()){int x=dl.front();dl.pop();for (int i=tree.ls[x];i;i=tree.a[i].next){int y=tree.a[i].to;if (dep[y]) continue;dl.push(y);f[y][0]=x;dep[y]=dep[x]+1;dis[y][0]=tree.a[i].w;}}
}
int LCA(int x,int y)//树上倍增
{int ans=2147483647;if (dep[x]>dep[y]) swap(x,y);for (int i=t;i>=0;i--)if (dep[f[y][i]]>=dep[x])ans=min(ans,dis[y][i]),y=f[y][i];if (x==y) return ans;for (int i=t;i>=0;i--)if (f[y][i]!=f[x][i]) {ans=min(ans,min(dis[x][i],dis[y][i]));//统计x=f[x][i];y=f[y][i];}ans=min(ans,min(dis[x][0],dis[y][0]));return ans;
}
int main()
{scanf("%d%d",&n,&m);for (int i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&z);p.addl(x,y,z);p.addl(y,x,z);}sort(p.a+1,p.a+1+p.tot,cmp);for (int i=1;i<=n;i++)father[i]=i;for (int i=1;i<=p.tot;i++){if (find(p.a[i].to)!=find(p.a[i].from)){s++;tree.addl(p.a[i].to,p.a[i].from,p.a[i].w);tree.addl(p.a[i].from,p.a[i].to,p.a[i].w);father[find(p.a[i].to)]=find(p.a[i].from);if (s==n) break;}}//kruskalt=(int)(log(n)/log(2))+1;for (int i=1;i<=n;i++)if (!dep[i]) {dis[i][0]=2147483647;bfs(i);//广搜预处理}for (int j=1;j<=t;j++){for (int i=1;i<=n;i++){f[i][j]=f[f[i][j-1]][j-1];dis[i][j]=min(dis[i][j-1],dis[f[i][j-1]][j-1]);}}//计算树上倍增与统计路径最小值的预处理scanf("%d",&q);for (int i=1;i<=q;i++){scanf("%d%d",&x,&y);if (find(x)!=find(y))printf("-1\n");//不连通elseprintf("%d\n",LCA(x,y));//输出}
}