题意:
链接:https://vjudge.net/problem/UVA-11354
给你 n 个点,m 条边无向边,每条边告诉起点和终点还有权值,q次询问,每次询问从 s 点到 t 点的路径上的最小的最大权值(最大权值最小化)
解题思路:
首先如果把这个无向图建成一个最小生成树,那么在最小生成树的任意两点有仅有一条路,并且也使得最大权值最小化了,但是每次都要花费 o(n) 的时间搜索,时间复杂度为 : Q * N 爆掉了,所以可以用按秩合并(每个节点秩最大为 floor(lgn) )的并查集来做,时间复杂度为 Q * lg(n) ,经过秩优化的并查集,查询的时间为 lg(n) ,具体证明算法导论 P332 左上角。(秩可以理解为就是树的深度,一个孤立节点的秩为 0)
注 : 为了保证留住边的路经,固然不可以路径压缩
AC代码:
#include<bits/stdc++.h>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;
int n, m;
int pre[maxn], cost[maxn], dep[maxn], vis[maxn];
struct node
{
int u, v, w;
}a[maxn];
int find_(int x)
{
while(x != pre[x]) x = pre[x];
return x;
}
int cmp(node a, node b)
{
return a.w < b.w;
}
void build(int x, int y, int w)
{
int fx = find_(x), fy = find_(y);
if(fx == fy) return ;
if(dep[fx] < fy) // 按秩合并,秩小的连到秩大的
{
pre[fx] = fy;
cost[fx] = w;
}
else if(dep[fx] > fy)
{
pre[fy] = fx;
cost[fy] = w;
}
else
{
pre[fx] = fy; // 如果秩相同,随便连
dep[fy]++; // 记录每个节点秩的大小
cost[fx] = w;
}
return ;
}
void init()
{
for(int i = 1; i <= n; i++)
{
cost[i] = 0;
dep[i] = 0;
pre[i] = i;
}
for(int i = 1; i <= m; i++)
{
scanf("%d %d %d", &a[i].u, &a[i].v, &a[i].w);
}
sort(a + 1, a + 1 + m, cmp);
for(int i = 1; i <= m; i++)
{
build(a[i].u, a[i].v, a[i].w);
}
}
int query(int s, int t)
{
int ans, cur;
int boos = find_(s);
int cost_s = 0, cost_t = 0;
cur = s;
vis[cur] = 0;
while(cur != boos) // 先从s点找到根节点
{
cost_s = max(cost_s, cost[cur]);
cur = pre[cur];
vis[cur] = cost_s;
}
cur = t;
while(1) // 找最进公共祖先
{
if(vis[cur] > -1)
{
ans = max(cost_t, vis[cur]);
break;
}
cost_t = max(cost_t, cost[cur]);
cur = pre[cur];
}
cur = s;
while(1) // 动态清 vis 数组,如果每次计算使用 memset 会T
{
vis[cur] = -1;
if(cur == boos) break;
cur = pre[cur];
}
return ans;
}
int main()
{
int case_ = 0;
memset(vis, -1, sizeof(vis));
while(~scanf("%d %d", &n, &m))
{
init();
if(case_) puts("");
case_++;
int q; scanf("%d", &q); while(q--)
{
int x, y; scanf("%d %d", &x, &y);
printf("%d\n", query(x, y));
}
}
}