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;
}
posted @ 2023-06-23 22:14  alex_liu09  阅读(13)  评论(0)    收藏  举报