题目传送门
题目描述:
A国有n座城市,编号从 1到n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入输出格式
输入格式:
第一行有两个用一个空格隔开的整数n,m表示 A 国有n 座城市和 m 条道路。
接下来 m行每行3个整数 x,y,z每两个整数之间用一个空格隔开,表示从 x号城市到y号城市有一条限重为 z 的道路。注意: x 不等于 y,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式:
共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出−1。
分析:因为两个点之间的距离是路径中的最小的权值,想要这个权值最大,那么就是最小的最大,这时我们想到了最大生成树。我们用跑最大生成树重新建图,然后在新图上跑倍增LCA记录权值和所对应的父亲即可。
我们尝试用 表示 第i个点以上 辈的点是什么,再用 表示第i个点向上 辈的最小权值是什么(最小的最大)
我们不难推出以下公式:
我们只需要预处理出这两个公式在树上跑一遍倍增求LCA即可
那么具体代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[100010][20];
int f[100010];
bool vis[100010];
int mo[100010][20];
struct node{
int x,y,v;
}e1[1000010];
struct node1{
int y,Next,v;
}e[1000100];
int cnt=0,len=0;
int linkk[100010];
int d[100010];
int d1[100010];
bool mycmp(node x,node y){
return x.v>y.v;
}
int getfa(int k){
return k==f[k]?k:f[k]=getfa(f[k]);
}
void insert(int x,int y,int v){
e[++len].Next=linkk[x];
linkk[x]=len;
e[len].y=y;
e[len].v=v;
}
void dfs(int x,int de){
if (vis[x]) return;
vis[x]=1;
d[x]=de;
for (int i=linkk[x];i;i=e[i].Next){
int y=e[i].y;
if (y==fa[x][0]) continue;
fa[y][0]=x;
mo[y][0]=e[i].v;
dfs(y,de+1);
}
}
void find_fa(){
for (int j=1;j<=19;j++)
for (int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1],mo[i][j]=min(mo[i][j-1],mo[fa[i][j-1]][j-1]);
}
int lca(int u,int v){
if (getfa(u)!=getfa(v)) return -1;
int ans=10000000;
if (d[u]>d[v]) swap(u,v);
for (int i=19;i>=0;i--)
if (d[fa[v][i]]>=d[u]) ans=min(ans,mo[v][i]),v=fa[v][i];
if (u==v) return ans;
for (int i=19;i>=0;i--)
if (fa[u][i]!=fa[v][i]) ans=min(ans,min(mo[u][i],mo[v][i])),u=fa[u][i],v=fa[v][i];
return min(ans,min(mo[u][0],mo[v][0]));
}
int main(){
scanf("%d %d",&n,&m);
for (int i=1,x,y,z;i<=m;i++) scanf("%d %d %d",&x,&y,&z),e1[++cnt]=(node){x,y,z},e1[++cnt]=(node){y,x,z};
sort(e1+1,e1+cnt+1,mycmp);
for (int i=1;i<=n;i++) f[i]=i;
for (int i=1;i<=cnt;i++){
int x=getfa(e1[i].x),y=getfa(e1[i].y);
if (x!=y)
f[x]=y,insert(x,y,e1[i].v),insert(y,x,e1[i].v);//最大生成树建边
}
int root=1;
for (int i=1;i<=n;i++)
if (!vis[i]){
d[i]=1;
dfs(i,1);
fa[i][0]=i;
mo[i][0]=10000000;
}//可能是森林,搜索未搜索的点,并计算出深度
find_fa();//预处理出fa数组
int q;
scanf("%d",&q);
for (int i=1,x,y;i<=q;i++) scanf("%d %d",&x,&y),printf("%d\n",lca(x,y));//输出它们最近公共祖先的权值,此时即最小的最大载重
}