P1967 NOIP2013 提高组 货车运输
题目描述
A 国有 \(n\) 座城市,编号从 \(1\) 到 \(n\),城市之间有 \(m\) 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 \(q\) 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入格式
第一行有两个用一个空格隔开的整数 \(n,m\),表示 A 国有 \(n\) 座城市和 \(m\) 条道路。
接下来 \(m\) 行每行三个整数 \(x, y, z\),每两个整数之间用一个空格隔开,表示从 \(x\) 号城市到 \(y\) 号城市有一条限重为 \(z\) 的道路。
注意: \(x \neq y\),两座城市之间可能有多条道路 。
接下来一行有一个整数 \(q\),表示有 \(q\) 辆货车需要运货。
接下来 \(q\) 行,每行两个整数 \(x,y\),之间用一个空格隔开,表示一辆货车需要从 \(x\) 城市运输货物到 \(y\) 城市,保证 \(x \neq y\)
输出格式
共有 \(q\) 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出 \(-1\)。
样例 #1
样例输入 #1
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
样例输出 #1
3
-1
3
提示
对于 \(30\%\) 的数据,\(1 \le n < 1000\),\(1 \le m < 10,000\),\(1\le q< 1000\);
对于 \(60\%\) 的数据,\(1 \le n < 1000\),\(1 \le m < 5\times 10^4\),\(1 \le q< 1000\);
对于 \(100\%\) 的数据,\(1 \le n < 10^4\),\(1 \le m < 5\times 10^4\),$1 \le q< 3\times 10^4 $,\(0 \le z \le 10^5\)。
分析
要使得货车装最重的货物,那么就必须货车走的路载重要尽可能大 ,也就是说,载重较小的路是不会被走过的。
于是就可以想到构建一棵原图的最大生成树。这样就可以在满足各城市之间连通情况不变的前提下,使得城市之间路载重最大。
然后:求货车能装最大货物重量的问题,就转化成了求树上两点权值最小的边的问题
如果用朴素算法求的话,显然是不行的,单次查询会被卡到 \(O(n)\)
那我们可以换一种思路:倍增求出 \(u,v\) 两点的 \(LCA\),设其为 \(t\)。
那么 \(u\) 到 \(v\) 的最小边权就是:\(u\) 到 \(t\) 的最小边权 和 \(v\) 到 \(t\) 的最小边权取较小值。
\(u,v\) 到 \(t\) 的最小边权又怎么求呢?
我们可以建一个数组 minw,其中 minw[i][j] 表示 \(i\) 节点到其 \(2^j\) 级祖先之间的最小边权。
这样就可以在求 LCA 的过程中得到答案了。
我们也容易推出 minw 的递推公式:
minw[i][j] = min(minw[i][j-1],minw[f[i][j-1]][j-1]);
对于不连通的两个点很好判断,用并查集判一下,如果不连通直接输出 \(-1\)。
如果两点连通:
我们需要记录答案 ans ,并初始化为正无穷(取一个足够大的整数即可)。
对于点 \(u\),倍增过程中每次跳到其 \(2^j\) 级节点时,进行操作:
ans = min(ans,minw[u][j]);
注意坑点:
给定的图可能有一些互不连通的城市群,在预处理的时候不要遗漏了。
代码中有两种 log 写法
#include<bits/stdc++.h>
#define int long long
#define inf INT_MAX
using namespace std;
struct node{int u,v,w;}e[50005];
vector<node>e2[10005];
bool vis[10005];
int n,m,q;
int fa[10005];
int f[10005][15],dep[10005],lg[10005],minw[10005][15];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
bool cmp(node x,node y){return x.w>y.w;}
void kruskal(int cnt=n-1){
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++){
if(!cnt)break;
int u=find(e[i].u),v=find(e[i].v);
if(u^v){
fa[u]=v,--cnt;
e2[e[i].u].push_back({e[i].u,e[i].v,e[i].w});
e2[e[i].v].push_back({e[i].v,e[i].u,e[i].w});
}
}
}
void dfs(int u,int fath){
vis[u]=1,dep[u]=dep[fath]+1,f[u][0]=fath;
for(int i=1;i<=lg[dep[u]];i++){
minw[u][i]=min(minw[u][i-1],minw[f[u][i-1]][i-1]);
f[u][i]=f[f[u][i-1]][i-1];
}
for(int i=0;i<e2[u].size();i++){
if(e2[u][i].v^fath)minw[e2[u][i].v][0]=e2[u][i].w,dfs(e2[u][i].v,u);
}
}
int lca(int x,int y,int res=inf){
if(find(x)^find(y))return -1;
if(dep[x]<dep[y])swap(x,y);
while(dep[x]>dep[y]){
res=min(res,minw[x][lg[dep[x]-dep[y]]]);
x=f[x][lg[dep[x]-dep[y]]];
}
if(x==y)return res;
for(int i=lg[dep[x]];i>=0;i--){
if(f[x][i]^f[y][i]){
res=min(res,min(minw[x][i],minw[y][i]));
x=f[x][i],y=f[y][i];
}
}
res=min(res,min(minw[x][0],minw[y][0]));
return res;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)cin>>e[i].u>>e[i].v>>e[i].w;
fa[1]=1;
for(int i=2;i<=n;i++)lg[i]=lg[i-1]+((1<<lg[i-1]+1)==i),fa[i]=i;
// for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1,fa[i]=i;
kruskal();
for(int i=1;i<=n;i++){
if(!vis[i])minw[i][0]=inf,dfs(i,0);
}
cin>>q;
for(int i=1,x,y;i<=q;i++){
cin>>x>>y;
cout<<lca(x,y)<<endl;
}
return 0;
}

浙公网安备 33010602011771号