P1967 货车运输 题解
Posted bifanwen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1967 货车运输 题解相关的知识,希望对你有一定的参考价值。
简要题意:
给定一个无向图,若干组询问问 (x ightarrow y) 所有路径上最小权值的最大值。
算法一
对于 (60\%) 的数据,(1 le n < 10^3,1 le m < 5 imes 10^4,1 le q< 10^3)
如果您有网络流板子的话,可以认为,本题是 给定网络图,若干组流量询问,直接 ( ext{ISAP}) 乱跑就可以得到 (O(qnm)) 的优秀时间,轻松得到 (60pts) 及以上。(网络流算法时间跑不满)
说句闲话:这题网络流怎么优化
时间复杂度:(O(qnm)). 期望得分:(60pts). 实际得分 100pts
算法二
对于 (100\%) 的数据,(1 le n < 10^4 , 1 le m < 5 imes 10^4 , 1 le q< 3 imes 10^4 , 0 le z le 10^5).
首先,我们考虑的是,如果,(x ightarrow u (w_1)) , (u ightarrow v (w_2)) ,与 (x ightarrow v (w_3)) 同时存在的话,如果 (min(w_1,w_2) leq w_3),可以直接把 (w_3) 扔掉;走的时候肯定是走 (x ightarrow w ightarrow v) 了。
那么,我们得到了网络流的贪心算法 显然,我们需要对每一组 (x
ightarrow y) 都把这样的 废边 去掉。
嗯?所以我们要把所有 (x ightarrow y) 的最大路径取出来,对吧?
那么,最大生成树 就映入我们眼帘:显然这是 最大生成树重构图 !
求最大生成树并只留下这个树上的边(重构),那么 (x ightarrow y) 肯定是最大路径(不然肯定换边了)。
那么如何应对询问?显然,我们在 树上求两点路径 用 ( ext{LCA}) 操作即可。
注意细节。
时间复杂度:(O(n log n + q log n + m)).
实际得分:(100pts).
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
inline int read(){char ch=getchar(); int f=1;while(ch<‘0‘ || ch>‘9‘) {if(ch==‘-‘) f=-f; ch=getchar();}
int x=0;while(ch>=‘0‘ && ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar();return x*f;}
struct EE1 {int x,y,dis;} E1[N];
struct E2 {int to,nxt,w;} E2[N];
int head[N],n,m,f[N],deep[N],cnt,q;
int fa[N][21],w[N][21]; bool vis[N];
inline void add(int u,int v,int w) {
if(u==v) return;
E2[++cnt].nxt=head[u];
E2[cnt].to=v; E2[cnt].w=w;
head[u]=cnt; return;
}
inline int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
inline bool cmp(EE1 x,EE1 y) {return x.dis>y.dis;}
inline void kruskal() {
sort(E1+1,E1+1+m,cmp);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++) {
int u=E1[i].x,v=E1[i].y,w=E1[i].dis;
if(find(u)-find(v)) {
f[find(u)]=find(v);
add(u,v,w); add(v,u,w);
}
}
} //最大生成树模板
inline void dfs(int u) {
vis[u]=1;
for(int i=head[u];i;i=E2[i].nxt) {
int v=E2[i].to,t=E2[i].w;
if(vis[v]) continue;
deep[v]=deep[u]+1;
fa[v][0]=u; w[v][0]=t;
dfs(v);
} return;
} //预处理父亲
inline int LCA(int x,int y) {
if(find(x)-find(y)) return -1;
int ans=INT_MAX;
if(deep[x]>deep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(deep[fa[y][i]]>=deep[x]) ans=min(ans,w[y][i]),y=fa[y][i]; //调整深度
if(x==y) return ans;
for(int i=20;i>=0;i--)
if(fa[x][i]-fa[y][i]) {
ans=min(ans,min(w[x][i],w[y][i]));
x=fa[x][i]; y=fa[y][i];
} //一起跳
return min(ans,min(w[x][0],w[y][0])); //最后答案
}
int main() {
n=read(),m=read();
for(int i=1;i<=m;i++) {
int u=read(),v=read(),w=read();
// add(u,v,w); add(v,u,w);
E1[i].x=u; E1[i].y=v; E1[i].dis=w;
} kruskal();
for(int i=1;i<=n;i++)
if(!vis[i]) {
deep[i]=1; dfs(i);
fa[i][0]=i; w[i][0]=INT_MAX;
} q=read();
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++) {
fa[j][i]=fa[fa[j][i-1]][i-1]; //fa[j][i] 为 j 向上走 2^i 个节点到达的节点
w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]); //w[j][i] 为 j 向上走 2^i 个节点经过路径的权值最小值
} while(q--) printf("%d
",LCA(read(),read()));
return 0;
}
以上是关于P1967 货车运输 题解的主要内容,如果未能解决你的问题,请参考以下文章