0w1

UVA 11354 Bond ( 最小瓶頸路徑 Kruskal + LCA )

UVa Online Judge
題目是很裸的最小瓶頸路徑,一般的做法是 O( n * n )
生成樹相關問題 -- 訓練指南 - 0w1
但這題 n ≤ 50000,行不通,這時候就要使用比較難寫的最小瓶頸路徑,先進行一些預處理降到 O( n lg n ) 級別。注意求最小瓶頸路徑時我們用的 maxcost二維陣列,其實是一種 RMQ,令 cost( v )為 v 與其父節點連結的邊權,那麼 maxcost[ a ][ b ] 其實就等價於
max{ max{ cost( p ) | Ɐ p ∈ shortestPath[ a, lca( a, b ) ) }, max{ cost( q ) | Ɐ q ∈ shortestPath[ b, lca( a, b ) ) } }
於是我們發現 maxcost陣列可以在計算 LCA時一併處理,利用 doubling的算法解決。
小小領悟:像這樣好幾個算法一起用會亂,所以開個 struct把獨有的變數包進來會比較不礙手礙腳。全域變數盡量只留輸入的就可以了。
碎念:又是一題可樹鏈剖分,想早點學啊。。
uva 11354 - Bond(树链剖分) - 不慌不忙、不急不躁 - 博客频道 - CSDN.NET

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 1e5 + 5;
const int MAXLGN = 20;
const int INF = 1e9;

int kase;
int n, m, q;
struct Edge{
    int u, v, c;
    bool operator < (const Edge &oth){
        return c < oth.c;
    }
} es[MAXM];
vector<Edge> G[MAXN];

struct UnionFind{
    int fa[MAXN];
    void init(int n){ for(int i = 0; i <= n; ++i) fa[i] = i; }
    int find(int x){ return fa[x] == x ? x : fa[x] = find( fa[x] ); }
    bool unite(int a, int b){
        int x = find( a ), y = find( b );
        if( x == y ) return false;
        fa[ x ] = y; return true;
    }
} uf;

struct Kruskal{
    void solve(){
        sort( es, es + m );
        uf.init( n );
        for(int i = 1; i <= n; ++i) G[i].clear();
        for(int i = 0; i < m; ++i){
            int u = es[i].u, v = es[i].v, c = es[i].c;
            if( uf.unite( u, v ) ){
                G[ u ].push_back( (Edge){ u, v, c } );
                G[ v ].push_back( (Edge){ v, u, c } );
            }
        }
    }
} mst;

struct LCA{
    int depth[MAXN], fa[MAXN], cost[MAXN];
    int anc[MAXLGN][MAXN], maxcost[MAXLGN][MAXN];
    void preprocess(){
        memset( anc, -1, sizeof(anc) );
        memset( maxcost, -1, sizeof(maxcost) );
        for(int u = 1; u <= n; ++u){
            anc[0][u] = fa[u];
            maxcost[0][u] = cost[u];
        }
        for(int i = 1; ( 1 << i ) <= n; ++i)
            for(int u = 1; u <= n; ++u)
                if( anc[i - 1][u] != -1 ){
                    int k = anc[i - 1][u];
                    if( anc[i - 1][k] == -1 ) continue;
                    anc[i][u] = anc[i - 1][k];
                    maxcost[i][u] = max( maxcost[i - 1][u], maxcost[i - 1][k] );
                }
    }
    int query(int p, int q){
        if( depth[ p ] < depth[ q ] ) swap( p, q );
        int hibit;
        for(hibit = 1; ( 1 << hibit ) <= depth[p]; ++hibit); --hibit;
        int ans = -INF;
        for(int i = hibit; i >= 0; --i)
            if( depth[ p ] - ( 1 << i ) >= depth[ q ] ){
                ans = max( ans, maxcost[i][p] );
                p = anc[i][p];
            }
        if( p == q ) return ans;
        for(int i = hibit; i >= 0; --i)
            if( anc[i][p] != -1 && anc[i][p] != anc[i][q] ){
                ans = max( ans, maxcost[i][p] ); p = anc[i][p];
                ans = max( ans, maxcost[i][q] ); q = anc[i][q];
            }
        ans = max( ans, cost[p] );
        ans = max( ans, cost[q] );
        return ans;
    }
} lca;

void dfs(int u, int fa, int depth){
    lca.depth[u] = depth;
    for(Edge &e: G[u]){
        if( e.v == fa ) continue;
        lca.fa[ e.v ] = u;
        lca.cost[ e.v ] = e.c;
        dfs( e.v, u, depth + 1 );
    }
}

void solve(){
    mst.solve();
    dfs( 1, -1, 0 );
    lca.preprocess();
    scanf("%d", &q);
    while( q-- ){
        int x, y; scanf("%d%d", &x, &y);
        printf("%d\n", lca.query( x, y ));
    }
}

int main(){
    while( scanf("%d%d", &n, &m) == 2 && n ){
        if( kase++ ) puts("");
        for(int i = 0; i < m; ++i){
            int u, v, c; scanf("%d%d%d", &u, &v, &c);
            es[i] = (Edge){ u, v, c };
        }
        solve();
    }
    return 0;
}