(Luogu) P1967 货车运输(kruscal + lca)

本文介绍了一种利用倍增算法解决城市间最大载重路径查询问题的方法。通过构建最大生成树并运用LCA算法,实现对任意两点间最大载重路径的有效查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题目大意:城市与城市之间有道路相连,并且道路有着限重,q次查询问,从x到y车的最大载重量,不能到达就输出-1.

解题思路:这道题有很多种解法,但这道题放在了倍增里,自然而然想着用倍增的方法去做,对于才初步了解这个算法的我来说,还是有点困难的。其实如果城市与城市之间有多条路径可以抵达,那只要保留那条载重最大的那条路径就行了。所以我们自然而然可以在它给的图上跑kruscal,跑出一颗最大生成树。然后在这颗树上我们就可以使用lca方法了,这是用来寻找最近公共祖先的,放到这里恰恰好。如果有最近的公共祖先那这两个城市就是相通的,反之无法到达。但要求最大载重量,那就还需要一个数组w[maxn][21],w[i][j],代表i节点到i的第2^j个父亲节点之间的最小载重量。

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
int fa[maxn],lg[maxn],dep[maxn],n,m;
int we[maxn][21],f[maxn][21];
bool vis[maxn];
struct edge {
	int s;
	int e;
	int w;
} ee[50005];
struct node {
	int to;
	int cost;
};
vector<node> G[10005];
bool cmp(edge x,edge y) {
	return x.w>y.w;
}
int find(int x) {
	if(fa[x]==x) return x;
	return fa[x] = find(fa[x]);
}
bool cmb(int x,int y) {
	x=find(x),y=find(y);
	if(x==y) return false;
	else {
		fa[x] = y;
		return true;
	}
}
void kruscal() {
	for(int i=1; i<=n+1; ++i)	fa[i]=i;
	for(int i=0; i<m; ++i) {
		int s=ee[i].s,e=ee[i].e,w=ee[i].w;
		if(cmb(s,e)) {
			G[s].push_back(node {e,w});
			G[e].push_back(node {s,w});
		}
	}
}
void dfs(int now,int last) {
	dep[now]=dep[last]+1;
	vis[now]=true;
	f[now][0]=last;
	for(int i=1; (1<<i)<=dep[now]; ++i) {
		we[now][i]=min(we[now][i-1],we[f[now][i-1]][i-1]);
		f[now][i]=f[f[now][i-1]][i-1];
	}
	for(int i=0; i<(int)G[now].size(); ++i) {
		if(G[now][i].to!=last) {
			we[G[now][i].to][0]=G[now][i].cost;
			dfs(G[now][i].to,now);
		}
	}
}
int lca(int x,int y) {
	if(find(x)!=find(y)) {
		return -1;
	}
	if(dep[x]>dep[y])	swap(x,y);
	int ans=inf;
	while(dep[x]!=dep[y]) {
		int len=dep[y]-dep[x];
		ans=min(ans,we[y][lg[len]-1]);
		y=f[y][lg[len]-1];
	}
	if(x==y)	return ans;
	for(int i=lg[dep[x]]-1; i>=0; --i) {
		if(f[x][i]!=f[y][i]) {
			ans=min(ans,min(we[x][i],we[y][i]));
			x=f[x][i];
			y=f[y][i];
		}
	}
	ans=min(ans,min(we[x][0],we[y][0]));
	return ans;
}
int main() {
	scanf("%d%d",&n,&m);
	int x,y,w;
	for(int i=0; i<m; ++i) {
		scanf("%d%d%d",&x,&y,&w);
		ee[i]=edge {x,y,w};
	}
	sort(ee,ee+m,cmp);
	kruscal();
	for(int i=1; i<=n; ++i) {
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	}

	for(int i=1; i<=n; ++i) {
		if(!vis[i]) {
			dfs(i,0);
		}
	}
	int t;
	scanf("%d",&t);
	for(int i=1; i<=t; ++i) {
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值