题目描述
A国有n座城市,编号从1到n,城市之间有m条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有q辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入输出格式
输入格式:
第一行有两个用一个空格隔开的整数n,m,表示A国有n座城市和m条道路。
接下来 mm行每行3个整数x, y, z,每两个整数之间用一个空格隔开,表示从x号城市到y号城市有一条限重为z的道路。注意:x不等于y,两座城市之间可能有多条道路 。
接下来一行有一个整数q,表示有q辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式:
共有q行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出−1。
输入输出样例
输入样例#1:
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例#1:
3
-1
3
题意
这道题的题意首先要读懂。
简单说,给你N个点,M条边的无向图,每条边有一个长度,现在有 Q个询问。
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最短的边最大值是多少?
分析
首先,题目中有很多无用的边,通过题意我们知道,要使得货车装最重的货物,那么就必须货车走的路载重要尽可能大 —— 也就是说,载重较小的路是不会被走过的。
通过这个,不难想到构建一颗最大生成树 ,这样就可以保证在满足各城市之间连通情况不变的前提下,使得城市之间路载重最大,于是题目就转变成,在树上两点之间求最小边权。
求出这个,问题来了,怎么算最小边权?首先想到的是暴力遍历每条边,但仔细一想这样做单次询问复杂度就会到O(N)去,明显不可取。
换一种思考方式,我们可以先求出A,B两点的最近公共祖先设为T,那问题又转化成,在A,T之间的最小边权和B,T之间的最小边权取最小值。
这样我们可以通过,lca的思想建一个maxv数组,其中 maxv[i][j]
表示 i 节点到其 2j 级祖先之间的最小边权。是不是很像倍增LCA。这样我们就可以在求LCA的过程中算出maxv数组。类比LCA的f数组的求法。不难得出,maxv[j][0]代表j与其父亲点的边权,于是maxv[j][i]就可已在预处理,时算出来。
状态转移方程如下:
maxv[j][i]=min(maxv[j][i-1],maxv[f[j][i-1]][i-1]);
最后是答案,我们用一个变量ans记录最小边权,将它初始化为一个极大值。如果两点不连通就用并查集判一下输出-1即可。在对于点 x,倍增过程中每次跳到其2j 级节点时,进行操作:
ans=min(ans,maxv[x][i]);
这样就可以得到答案ans, 那么这道问题,就愉快的解决啦~。
总结一下就是,重新建图,构造出最大生成树,然后在最大生成树上求LCA的过程中,计算边权最小值。
坑点
注意,图中的点不一定联通,预处理时要注意一下。
代码
具体代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=500050;
int fa[N],n,m;
struct node{
int v,w,u;
}e[N];
vector<node> v[N];
bool vis[N];
int s[105],f[N][105];
int d[N],maxv[N][105];
int find(int x){
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
bool cmp(node a,node b){
return a.w>b.w;
}
void kru(){
for(int i=1;i<=m;i++){
int p=find(e[i].u),q=find(e[i].v);
if(p!=q){
fa[p]=q;
v[e[i].u].push_back({e[i].v,e[i].w});
v[e[i].v].push_back({e[i].u,e[i].w});
}
}
}
void dfs(int x,int dep){
d[x]=dep;
vis[x]=1;
for(int i=0;i<v[x].size();i++){
node y=v[x][i];
if(vis[y.v]) continue;
f[y.v][0]=x;
maxv[y.v][0]=y.w;
dfs(y.v,dep+1);
}
}
void pre(){
for(int i=1;s[i]<=n;i++)
for(int j=1;j<=n;j++){
f[j][i]=f[f[j][i-1]][i-1];
maxv[j][i]=min(maxv[j][i-1],maxv[f[j][i-1]][i-1]);
}
}
int lca(int x,int y){
if(find(x)!=find(y)) return -1;
int ans=INT_MAX;
if(d[x]>d[y]) swap(x,y);
int k=log2(d[y])+1;
for(int i=k;i>=0;i--)
if(d[y]-s[i]>=d[x]){
ans=min(ans,maxv[y][i]);
y=f[y][i];
}
if(x==y) return ans;
for(int i=k;i>=0;i--)
if(f[x][i]!=f[y][i]){
ans=min(ans,min(maxv[y][i],maxv[x][i]));
x=f[x][i];
y=f[y][i];
}
ans=min(ans,min(maxv[y][0],maxv[x][0]));
return ans;
}
signed main(){
s[0]=1;
for(int i=1;i<=20;i++)
s[i]=s[i-1]*2;
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>e[i].u>>e[i].v>>e[i].w;
for(int i=1;i<=n;i++)
fa[i]=i;
sort(e+1,e+m+1,cmp);
kru();
for(int i=1;i<=n;i++)
if(!vis[i]){
dfs(i,1);
f[i][0]=i;
maxv[i][0]=INT_MAX;
}
pre();
int q;
cin>>q;
while(q--){
int x,y;
cin>>x>>y;
cout<<lca(x,y)<<"\n";
}
return 0;
}
结语
如果您觉得这篇文章对您有帮助,麻烦点个赞再走吧。