UVA 11354 - 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值