洛谷 - P1967 货车运输
最大生成树+lca。
参考资料
思路
首先便是想到了Floyd的暴力方法,状态转移方程也不难推出:w[i][j]=max(w[i][j], min(w[i][k],w[k][j]));但是n3次方时间复杂度和n2的空间复杂度是显然不可取的。
于是我们思考,可以发现有一些权值较小的边是不会被走过的。正如样例中的第三条边,就算有其他的很多条边,这条边无论如何也是不会被走过的。于是我们想到了可以将图中这样的边去掉,按照这个思路我们便想到了构造最大生成树,将其余的边去除。
得到了这样一个树之后,我们便考虑如何求出两个节点之间最小边权的最大值(即为题中的最大载重),因为这两点之间的路径是唯一的,我们只需要找出这条路径便可以得到答案。我们可以通过LCA来做到这一点,先从每一个根节点进行搜索,求出节点深度等信息,然后利用这些信息进行树上倍增。
于是我们可以得出大体思路:首先重新建图,构造出最大生成树,然后在最大生成树上求LCA来回答询问。
代码
最可恶的是忘换行了……
#include<bits/stdc++.h>
#define INF 2000000000
using namespace std;
struct bian1 {
int x,y,z;
} bn1[50005];
struct bian2 {
int to,next,w;
} bn2[100005];
int cnt,n,m,head[10005],deep[10005],f[10005],fa[10005][21],w[10005][21];
bool vis[10005];
void adlln(int from, int to, int w){
bn2[++cnt].next=head[from];
bn2[cnt].to=to;
bn2[cnt].w=w;
head[from]=cnt;
return;
}
bool CMP(bian1 x, bian1 y){
return x.z>y.z;
}
int find(int x){
if(f[x]!=x)f[x]=find(f[x]);
return f[x];
}
void kruskal(){
sort(bn1+1, bn1+m+1, CMP);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++){
if(find(bn1[i].x)!=find(bn1[i].y)){
f[find(bn1[i].x)]=find(bn1[i].y);
adlln(bn1[i].x, bn1[i].y, bn1[i].z);
adlln(bn1[i].y, bn1[i].x, bn1[i].z);
}
}
return;
}
void dfs(int node){
vis[node]=true;
for(int i=head[node];i;i=bn2[i].next){
int to=bn2[i].to;
if(vis[to])continue;
deep[to]=deep[node]+1;
fa[to][0]=node;
w[to][0]=bn2[i].w;
dfs(to);
}
return;
}
int lca(int x, int y){
if(find(x)!=find(y))return -1;
int ans=INF;
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];
}
ans=min(ans, min(w[x][0], w[y][0]));
return ans;
}
int main(){
int x,y,z,q;
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)cin>>bn1[i].x>>bn1[i].y>>bn1[i].z;
kruskal();
for(int i=1;i<=n;i++){
if(!vis[i]){
deep[i]=1;
dfs(i);
fa[i][0]=i;
w[i][0]=INF;
}
}
for(int i=1;i<=20;i++){
for(int j=1;j<=n;j++){
fa[j][i]=fa[fa[j][i-1]][i-1];
w[j][i]=min(w[j][i-1], w[fa[j][i-1]][i-1]);
}
}
cin>>q;
for(int i=1;i<=q;i++){
cin>>x>>y;
cout<<lca(x,y)<<'\n';
}
return 0;
}