LCA->火车运输(洛谷1967)
题目链接:https://www.luogu.com.cn/problem/P1967
题意:n个点,m条边的图。q个询问,问u->v之间权值最小的边权值是多少(不要求走最短路径,只要经过路径上经过的最小的边的权值最大)。
分析:如果两点间有路径,那么优先走边权更大的边,所以先利用最大生成树算法构建树。在构建了树之后,构建lca。对于每个点对u, v,找出他们的lca,并记录每个点到lca的路径上权值最小的边权值。
如果是森林,在dfs入口的地方写错了,起点应该是i。
不一定要用vis数组判定是否访问过,也可以用depth == -1。
大顶堆是less, <号。小顶堆是>, greater, >号。
void solve(){
int n, m;
cin >> n >> m;
priority_queue<Edge, vector<Edge>, less<Edge>> pq;
for (int i = 1; i <= m; ++i){
Edge t;
cin >> t;
pq.emplace(t);
}
DisjointSet dsu(n + 1);
vector<vector<pair<int, int>>> al(n + 1);
while (!pq.empty()){
auto[u, v, w] = pq.top();
pq.pop();
if (dsu.isSameSet(u, v)){
continue;
}
dsu.unionSet(u, v);
al[u].emplace_back(v, w);
al[v].emplace_back(u, w);
}
int p = upper_bound(pow2_values.begin(), pow2_values.end(), n) - pow2_values.begin() - 1;
vector<vector<int>> dp(n + 1, vector<int> (p + 1));
vector<int> depth(n + 1, -1);
vector<vector<int>> weight(n + 1, vector<int> (p + 1, 0x3f3f3f3f));
vector<int> vis(n + 1);
function<void(int, int, int)> dfs = [&](int u, int p, int d){
depth[u] = d;
dp[u][0] = p;
vis[u] = 1;
for (int i = 1; (1 << i) <= d; ++i){
dp[u][i] = dp[dp[u][i - 1]][i - 1];
weight[u][i] = min(weight[u][i - 1], weight[dp[u][i - 1]][i - 1]);
}
for (const auto& [v, w] : al[u]){
if (v != p && vis[v] == false){
weight[v][0] = w;
dfs(v, u, d + 1);
}
}
};
for (int i = 1; i <= n; ++i){
if (!vis[i]){
dfs(i, 0, 1);
}
}
auto cal = [&](int u, int v) -> int{
int res = int(2e9);
if (depth[u] < depth[v]){
swap(u, v);
}
for (int i = p; i >= 0; --i){
if (depth[u] - (1 << i) >= depth[v]){
res = min(res, weight[u][i]);
u = dp[u][i];
}
}
if (u == v){
return res;
}
for (int i = p; i >= 0; --i){
if (dp[u][i] != dp[v][i]){
res = min({res, weight[u][i], weight[v][i]});
u = dp[u][i];
v = dp[v][i];
}
}
//!注意,此时的u和v是祖先下面一层的节点
res = min({res, weight[u][0], weight[v][0]});
return res;
};
int q;
cin >> q;
while (q --){
int u, v;
cin >> u >> v;
if (dsu.isSameSet(u, v) == false){
cout << -1 << '\n';
continue;
}
cout << cal(u, v) << '\n';
}
}

浙公网安备 33010602011771号