洛谷:P1967 貨車運輸
阿新 • • 發佈:2018-09-28
一起 wap 隊列 是否為空 const 並查集 class 初始化 dfs
因此要求的新圖就是一顆最大生成樹……Kruskal可破。
然後就是求任意兩點LCA了。此處使用倍增,也方便維護某節點向樹根爬的時候路上的最小權值。
(用樹鏈剖分+線段樹維護也行…………)
更具體的看代碼註釋。
原題地址:https://www.luogu.org/problemnew/show/P1902
題目簡述
給定一個n個點m條邊的無向帶權圖,每次詢問2點u,v的聯通情況,不聯通則輸出-1。
如果聯通,不妨將一條聯通u,v的路徑上的最小權值記為w,則該次詢問輸出所有可能的w中的最大值。
共有q次詢問。
思路
對於任意兩點u&v,我們需要找出能使得w最大的一條最優路徑。
因此需要生成一個新圖,使得原圖中聯通任意兩點之間只存在一條能使得w最大的最優路徑。
因此這是一棵樹……
又因為要使w最大,應盡量選擇邊權大的邊作為路徑…… 然後就突然發覺:這不就是Kruskal算法的過程嗎?只不過最小生成樹優先選擇邊權小的邊,此時優先選擇邊權大的。
然後就是求任意兩點LCA了。此處使用倍增,也方便維護某節點向樹根爬的時候路上的最小權值。
(用樹鏈剖分+線段樹維護也行…………)
更具體的看代碼註釋。
代碼
#include<bits/stdc++.h> using namespace std; #define inf 1000000005 struct Node { int u,v,w;//兩點u&v以及邊權 bool operator < (const Node &b) const { return w<b.w; } }; vector <Node> e[50005];//e[u]儲存節點u相連的邊集。 priority_queue <Node> Q;//邊權越大的優先級越高 int fa[50005];//Kruskal的禦用並查集,fa[u]代表u點所處集合 bool vis[50005];//是否已經被dfs過程訪問過 int fas[50005][21],minw[50005][21],deep[50005]; //fas[u][j]代表u點在所處樹中的第2^j級父親編號 //minw[u][j]代表u點在所處樹中至第2^j級父親的路徑上最小邊權 //deep[u]代表u點在所處樹中深度 int find(int x)//查找x所在集合編號 { if (x==fa[x]) return x; else return fa[x]=find(fa[x]);//路徑壓縮 } void uni(int a,int b) //合並a,b所在集合 { fa[find(a)]=find(b); } void add(int u,int v,int w) //添加新圖邊 { Node one; one.u=u; one.v=v; one.w=w; e[u].push_back(one); uni(u,v); } void dfs(int u,int f,int k)//dfs,u代表當前點,f為當前點父親,k為深度 { vis[u]=1; deep[u]=k; for (int i=0;i<e[u].size();i++) { if (e[u][i].v==f) continue; else { dfs(e[u][i].v,u,k+1); fas[e[u][i].v][0]=u; minw[e[u][i].v][0]=e[u][i].w; } } } int n,m,q; void Kruskal() { int linked=0; while(!Q.empty()&&linked<n-1) {//邊數m可能少於n-1,因此需要註意Q是否為空 Node now=Q.top(); Q.pop(); int a=now.u,b=now.v; if (find(a)==find(b)) continue; else { linked++; add(a,b,now.w); add(b,a,now.w); } } } int lca(int x,int y)//求x,y的lca { if (find(x)!=find(y)) return -1;//不在一個樹裏 int ans=inf; if (deep[y] >deep[x]) swap(x,y);//較深的標記為x for(int i=20;i>=0;i--)//令x跳到與y相同高度 if(deep[fas[x][i]]>=deep[y]){ ans=min(ans,minw[x][i]); x=fas[x][i]; } if (x==y) return ans; for(int i=20; i>=0; i--)//讓x,y一起跳到lca節點下方 if(fas[x][i]!=fas[y][i]){ ans=min(ans,min(minw[x][i],minw[y][i])); x=fas[x][i]; y=fas[y][i]; } ans=min(ans,min(minw[x][0],minw[y][0]));//統計最小邊權 return ans; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) fa[i]=i;//並查集預處理,各個點都處於自己所代表的集合 for (int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); Node one; one.u=x; one.v=y; one.w=z; Q.push(one);//加入Kruskal禦用隊列Q } Kruskal();//最大生成樹 for (int i=1;i<=n;i++) {//倍增數組初始化&dfs if (!vis[i]) { dfs(i,0,1); fas[i][0]=i; minw[i][0]=inf; } } for (int i=1;i<=20;i++) {//倍增預處理 for (int j=1;j<=n;j++) { fas[j][i]=fas[fas[j][i-1]][i-1]; minw[j][i]=min(minw[j][i-1],minw[fas[j][i-1]][i-1]); } } scanf("%d",&q); for (int i=1;i<=q;i++) { int a,b; scanf("%d%d",&a,&b); printf("%d\n",lca(a,b)); } return 0; }
洛谷:P1967 貨車運輸