题目Luogu 4-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