ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题目Luogu 4-P1967 货车运输

2022-07-25 09:31:36  阅读:180  来源: 互联网

标签:cnt int Luogu 边权 ef 货车运输 father fa P1967


题目链接

依旧是很裸的题干


题干很清楚,肯定是图论

一开始想到单源最长路,用贝尔曼福德算法,全部边权变成相反数,后来发现不可行

因为这个题目要找的路径是边权最小值最大

最小值最大——二分

但是如果对于每个询问都二分一次再check,复杂度来到了O(n2logn)无法接受

(题解里好像有对询问排序做的nlogn二分,但是不会写,果然二分没学好)


那么应该怎么解决呢

想想如果我们是货车司机

只要不是傻子,肯定不会走载重(边权)小的路啊,哪怕绕远,只要经过的路径载重越大越好

那我们就从图里剔除边权小的且一定不会走过的边

边权小好说,怎么知道一定不会走过呢?

注意到给定的图可能有多个连通块,对于一个连通块,在剔边之后必须还是一个连通块且不能再剔除边

发现了什么?这是一个树!从小往大剔除,就是从大往小增加

由此得出第一步操作:建图,对于每个连通块跑最大生成树。

for(int i=1;i<=m;i++){
	int x,y,z;
	cin>>x>>y>>z;
	ef[cnt].u=x,ef[cnt].v=y,ef[cnt++].w=z;
}
sort(ef,ef+cnt,cmp);
int k=0;
for(int i=0;i<cnt;i++){
	if(father(ef[i].u)!=father(ef[i].v)){
		Union(ef[i].u,ef[i].v);
		e[ef[i].u].push_back(ef[i]);
		e[ef[i].v].push_back((E){ef[i].v,ef[i].u,ef[i].w});
		k++;
	}
	if(k==n-1)	break;
}

然后开始处理询问

既然跑最大生成树时用到了并查集,那么处理这个就好办了

如果货车不能到达目的地,输出 -1。

if(father(a)!=father(b))
		cout<<"-1"<<endl;

然后现在就能确保两个地点在一个最大生成树上,那怎么知道途径路径边权的最小值呢?

注意到树上两个点最短路径是唯一的,无论树怎样旋转

所以我们只需要让两个点一路向根移动,并同时记录边权最小值,直到在交汇点——它们的LCA,再对两个答案取min并输出

如何O(1)的查询到一个孩子节点到它父亲节点边的边权?用fapath[i]表示节点i的父节点到节点i的这条边,在<vector>e[i的父节点]中的下标

即在建边的时候,顺便记录fapath,查询的时候查询e[fa[now][0]][fapath[now]].w即可

至此,题目完整思路呈现:对每个连通块跑最大生成树,存树;对于每组询问,先用并查集判连通性,再在树上求LCA,遍历取min即可

完整代码

// Problem: P1967 [NOIP2013 提高组] 货车运输
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1967
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define INF 0x7fffffff
#define MAXN 10050
#define MAXM 50050
using namespace std;
struct E{
	int u,v,w;
}ef[MAXM];
vector<E> e[MAXN];
int n,m,cnt;
int jh[MAXN],fa[MAXN][21],depth[MAXN],fapath[MAXN];
int father(int x){
	return jh[x]==0?x:jh[x]=father(jh[x]);
}
void Union(int x,int y){
	int fx=father(x),fy=father(y);
	if(fx!=fy)	jh[fx]=fy;
}
bool cmp(E p,E q){
	return p.w>q.w;
}
void dfs(int now,int fath){
	if(fath!=-1){
		fa[now][0]=fath;
		depth[now]=depth[fath]+1;
		for(int i=1;i<=log2(depth[now]);i++){
			fa[now][i]=fa[fa[now][i-1]][i-1];
		}
	}
	for(int i=0;i<e[now].size();i++){
		if(e[now][i].v!=fath){
			fapath[e[now][i].v]=i;
			dfs(e[now][i].v,now);
		}
	}
}
int LCA(int x,int y){
	if(depth[x]<depth[y]){
		swap(x,y);
	}
	while(depth[x]>depth[y]){
		x=fa[x][(int)log2(depth[x]-depth[y])];
	}
	if(x==y)	return y;
	for(int i=log2(depth[x]);i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
int getans(int LCA,int x,int y){
	int ans=INF;
	while(x!=LCA){
		ans=min(ans,e[fa[x][0]][fapath[x]].w);
		x=fa[x][0];
	}
	while(y!=LCA){
		ans=min(ans,e[fa[y][0]][fapath[y]].w);
		y=fa[y][0];
	}
	return ans;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		ef[cnt].u=x,ef[cnt].v=y,ef[cnt++].w=z;
	}
	sort(ef,ef+cnt,cmp);
	int k=0;
	for(int i=0;i<cnt;i++){
		if(father(ef[i].u)!=father(ef[i].v)){
			Union(ef[i].u,ef[i].v);
			e[ef[i].u].push_back(ef[i]);
			e[ef[i].v].push_back((E){ef[i].v,ef[i].u,ef[i].w});
			k++;
		}
		if(k==n-1)	break;
	}
	int q;
	cin>>q;
	for(int i=1;i<=n;i++){
		if(father(i)==i){
			depth[i]=1;
			dfs(i,-1);
		}
	}
	while(q--){
		int a,b;
		cin>>a>>b;
		if(father(a)!=father(b)){
			cout<<"-1"<<endl;
		}else{
			cout<<getans(LCA(a,b),a,b)<<endl;
		}
	}
	return 0;
}

 

标签:cnt,int,Luogu,边权,ef,货车运输,father,fa,P1967
来源: https://www.cnblogs.com/XHZS-XY/p/16516277.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有