1. 程式人生 > >Bond(並查集-按秩合併)

Bond(並查集-按秩合併)

題意:給出一張n個點m條邊的無向圖, 每條邊有一個危險度,有q個詢問, 每次給出兩個點s、t,找一條路, 使得路徑上的最大危險度最小。

思路:首先,我們可以發現,如果求一個最小生成樹, 那麼任意兩點, 在生成樹上有唯一路徑, 而且這條路徑上的最大危險值一定最小。 但是n和q都太大, 如果直接順著樹走,每次詢問最大複雜度O(n), 那麼複雜度高達O(n^2),會超時。   我們知道, 並查集在用了路徑壓縮之後效率高達O(n), 但是卻破壞了樹形結構, 所以不能用路徑壓縮。  然而我們經常忽視了按秩合併這個方法, 即使不用路徑壓縮, 僅僅靠按秩合併, 複雜度也可低至O(logn)。  因此我們只需按秩合併, 然後詢問的時候向根回溯就行了, 複雜度mlogn。

細節參見程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000 + 7;
const int maxn = 50000 + 10;
int T,n,m,p[maxn],ra[maxn],edge[maxn],vis[maxn];
struct node {
    int a, b, c;
    bool operator < (const node& rhs) const {
        return c < rhs.c;
    }
}a[100000 + 10];
int _find(int x) { return p[x] == x ? x : _find(p[x]); }
void Union(int a, int b, int c) {
    int x = _find(a);
    int y = _find(b);
    if(x == y) return ;
    if(ra[x] < ra[y]) {
        p[x] = y;
        edge[x] = c;
    }
    else {
        p[y] = x;
        edge[y] = c;
        if(ra[x] == ra[y]) ra[x]++;
    }
}
void pre() {
    sort(a, a+m);
    for(int i=1;i<=n;i++) {
        p[i] = i;
        ra[i] = 0;
        vis[i] = -1;
    }
    for(int i=0;i<m;i++) {
        Union(a[i].a,a[i].b,a[i].c);
    }
}
int query(int x, int y) {
    int ans1 = 0, ans2 = -1;
    int cur = x;
    while(true) {
        vis[cur] = ans1;
        if(cur == p[cur]) break;
        ans1 = max(ans1, edge[cur]);
        cur = p[cur];
    }
    cur = y;
    while(true) {
        if(vis[cur] >= 0) {
            ans2 = max(ans2, vis[cur]); break;
        }
        if(cur == p[cur]) break;
        ans2 = max(ans2, edge[cur]);
        cur = p[cur];
    }
    cur = x;
    while(true) {
        vis[cur] = -1;
        if(cur == p[cur]) break;
        cur = p[cur];
    }
    return ans2;
}
int u,v,q,kase=0;
int main() {
    while(~scanf("%d%d",&n,&m)) {
        for(int i=0;i<m;i++) {
            scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c);
        }
        if(kase) printf("\n");
        else ++kase;
        pre();
        scanf("%d",&q);
        while(q--) {
            scanf("%d%d",&u,&v);
            printf("%d\n",query(u, v));
        }
    }
    return 0;
}