帶權LCA【洛谷P1967】
阿新 • • 發佈:2018-11-21
傳送門:https://www.luogu.org/problemnew/show/P1967
帶權LCA,模板題。
和fa[now][0]一樣,w[now][0]表示父節點的權值。
更新w[now][i]的時候要從w[now][0]開始更新,因為上一次記錄的是w[now][0],因為這個沒寫好錯了很多次。
然後就是結點往上爬的時候更新w權值。
奧奧,為什麼這個題跑一下LCA就可以了呢。
題目要求是找一條最小邊的最大值,dijkstra+二分直接炸了,只有10分,跑了2W MS。
下面說說演算法的正確性。
既然是最小邊的最大值,那我們就直接按照邊權排序構造一個最大生成樹,這樣顯然可以保證最小值最大。
然後我們在最大生成樹上跑LCA,因為在最大生成樹上,任意兩個點之間有且僅有一條路徑,所以我們從起點和終點找LCA,然後更新一下兩邊最小的邊權,就相當於從起點到終點的邊權的最小值啦。
細節比較多,其實也沒啥難度(裝傻)
下面上程式碼:
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int maxn = 1e5+7; const int maxl = 21; int n,m; int depth[maxn],fa[maxn][maxl]; int w[maxn][maxl]; int lg[maxn]; int vis[maxn]; int p[maxn]; struct node { int from; int to; int cost; }q[maxn]; struct edge { int to; int cost; }; vector<edge> G[maxn]; bool cmp(node a,node b) { return a.cost>b.cost; } void makeset() { for(int i=0;i<maxn;i++) { p[i] = i; } } int find(int x) { if(x==p[x]) return x; return p[x] = find(p[x]); } void unite(int x,int y) { x = find(x); y = find(y); if(x==y) return; p[x] = y; } bool same(int x,int y) { return find(x)==find(y); } void kruskal() { makeset(); sort(q+1,q+1+m,cmp); for(int i=1;i<=m;i++) { if(same(q[i].from,q[i].to)) continue; unite(q[i].from,q[i].to); G[q[i].from].push_back({q[i].to,q[i].cost}); G[q[i].to].push_back({q[i].from,q[i].cost}); } } void dfs(int now,int last) { vis[now] = 1; depth[now] = depth[last]+1; fa[now][0] = last; for(int i=1;(1<<i)<=depth[now];i++) { w[now][i] = min(w[now][i-1],w[fa[now][i-1]][i-1]); fa[now][i] = fa[fa[now][i-1]][i-1]; } for(int i=0;i<G[now].size();i++) { if(G[now][i].to!=last) { w[G[now][i].to][0] = G[now][i].cost; dfs(G[now][i].to,now); } } } int lca(int x,int y) { if(!same(x,y)) return -1; if(depth[x]>depth[y]) swap(x,y); int ans = INF; while(depth[x]!=depth[y]) { ans = min(ans,w[y][lg[depth[y]-depth[x]]-1]); y = fa[y][lg[depth[y]-depth[x]]-1]; } if(x==y) return ans; for(int i=lg[depth[x]]-1;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() { cin>>n>>m; for(int i=1;i<=m;i++) { cin>>q[i].from>>q[i].to>>q[i].cost; } kruskal(); for(int i=1;i<=n;i++) { lg[i] = lg[i-1]+(1<<lg[i-1]==i); } for(int i=1;i<=n;i++) { if(!vis[i]) { dfs(i,0); } } int q; cin>>q; for(int i=0;i<q;i++) { int x,y; cin>>x>>y; printf("%d\n",lca(x,y)); } return 0; }