P1967 [NOIP2013 提高組] 貨車運輸
題意簡述
給定一個\(N\)個節點,\(M\)條邊的無向圖,其中每條邊有一個邊權。
接下來給定\(q\)次詢問。每次詢問給出\(x,y\),請計算\(x\)到\(y\)路徑上最小邊權的最大值是多少。
解題思路
我們對於每個連通塊跑一遍最大生成樹。這樣整張圖就成了一片森林。
接下來對於森林裡的每一棵樹,跑一遍LCA,順便維護\(minn[pos][n]\)表示\(pos\)向上\(2^n\)層的最小邊權。查詢時,每跳一步更新最小邊權,最終返回最小邊權即可。
時間複雜度\(O(m\log m+q\log n)\)。
點選檢視程式碼
#include<bits/stdc++.h>
#define N 10010
#define M 50010
using namespace std;
int n,m,q,ffa[N],dep[N],fa[N][20],minn[N][20];
//ffa用於並查集,fa用於LCA
bool vis[N];
struct tedge{int u,v,w;}edges[M];
bool cmp(tedge a,tedge b){return a.w>b.w;}
int find(int x){
if(ffa[x]==x) return x;
return ffa[x]=find(ffa[x]);
}
struct edge{int to,w;};
vector<edge> G[N];
void dfs(int u,int father){
vis[u]=1;
dep[u]=dep[father]+1;
fa[u][0]=father;
for(int i=1;i<20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1],
minn[u][i]=min(minn[u][i-1],minn[fa[u][i-1]][i-1]);
for(auto i:G[u])
if(i.to!=father){
minn[i.to][0]=i.w;
dfs(i.to,u);
}
}
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
int ans=INT_MAX;
for(int i=19;i>=0;i--)
if(dep[fa[u][i]]>=dep[v])
ans=min(ans,minn[u][i]),
u=fa[u][i];
if(u==v) return ans;
for(int i=19;i>=0;i--)
if(fa[u][i]!=fa[v][i])
ans=min(ans,min(minn[u][i],minn[v][i])),
u=fa[u][i],v=fa[v][i];
ans=min(ans,min(minn[u][0],minn[v][0]));
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) ffa[i]=i;
for(int i=1;i<=m;i++)
cin>>edges[i].u>>edges[i].v>>edges[i].w;
sort(edges+1,edges+1+m,cmp);
for(int i=1;i<=m;i++){
int u=find(edges[i].u),v=find(edges[i].v);
if(u==v) continue;
ffa[u]=v;
G[edges[i].u].push_back({edges[i].v,edges[i].w});
G[edges[i].v].push_back({edges[i].u,edges[i].w});
}
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
cin>>q;
while(q--){
int x,y;
cin>>x>>y;
if(find(x)!=find(y)) cout<<"-1\n";
else cout<<lca(x,y)<<"\n";
}
return 0;
}