P2934 Safe Travel G
简要题意:
有一个 \(n\) 个点, \(m\) 条边的无向连通图。每个边有一条边权,保证 \(1\) 到其他每个点的最短路径唯一,对于每个 \(i\)(\(2\le i \le n\)),你需要求出当删去 \(1\) 与这个点的最短路径的最后一条边后新的最短路径长度。
解题思路:
我们首先发现我们可以根据最短路径构建出一棵根节点为 \(1\) 的树,使得 \(1\) 到各个节点的路径长度是它们在原图上的最短距离。
然后我们研究删除那条边后我们新的最短路径可以怎么构成。我们发现我们一定要借助非树边进行更新。但我们发现如果借助两次非树边那肯定是不优的,其中只有一个是有用的树边,另外一个不是因为他可以被其他最短路优化掉。则假设我们用于更新的树边为 \((u,v)\),则我们可以用这个树边更新从 \(u\) 到 \(LCA(u,v)\) 的点和 \(v\) 到 \(LCA(u,v)\) 的点(不含 \(LCA(u,v)\))。这个时候我们 \(i\) 点的最短路径长度就是 \(\min_{u,v}(d_u + w + d_v - d_i)\)(在这里,\(d_u\) 表示原图 \(1\) 到 \(u\) 的最短路径长度,也等同于生成树中 \(u\) 到根的距离)。
但是我们要是这样每个边都更新一遍的话最坏复杂度是有 \(O(nm)\) 的。我们考虑优化。
对于每个边,这个边 \((u,v)\) 自带的权是 \(d_u+w+d_v\),由于我们想要最小,则我们就按照非树边的权从小到大排序,这样被前面更新过的点就一定不需要被后面的点更新了,因为必然是不优的。
实现技巧
对于怎么实现被更新过的点怎么直接跳过这个事情,我们明显知道并查集是可以将路径直接压缩的。如果 \(u\) 这个点被更新过了,那么我们就直接把他和他的父节点合并。(这里是从下到上更新的)
Code:
#include <bits/stdc++.h>
#define ll long long
#define pii pair<int, int>
using namespace std;
const int N = 100010;
int n, m, tot, d[N], dep[N], fa[N][20], ans[N];
struct edge {int u, v, w;} e[N << 1];
bool cmp(edge a, edge b) {return d[a.u] + d[a.v] + a.w < d[b.u] + d[b.v] + b.w;}
int f[N];
int find(int u) {return f[u] == u ? u : f[u] = find(f[u]);}
struct Edge {int v, w;};
vector<Edge> g[N];
bool vis[N];
void dij() {
for(int i = 1; i <= n; i++) d[i] = 2e9;
priority_queue<pii, vector<pii>, greater<>>q;
q.push({0, 1}); d[1] = 0;
while(!q.empty()) {
int u = q.top().second; q.pop();
if(vis[u]) continue;
for(auto o : g[u]) {
int v = o.v, w = o.w;
if(d[u] + w < d[v]) {
d[v] = d[u] + w; q.push({d[v], v});
fa[v][0] = u; dep[v] = dep[u] + 1;
}
}
}
}
int lca(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
for(int i = 17; ~i; i--) if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];
if(u == v) return u;
for(int i = 17; ~i; i--) if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
g[u].push_back({v, w}); g[v].push_back({u, w});
}
dij();
for(int j = 1; j <= 17; j++)
for(int i = 1; i <= n; i++)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
for(int i = 1; i <= n; i++) {
ans[i] = -1; f[i] = i;
for(auto o : g[i]) {
int u = i, v = o.v, w = o.w;
if(fa[u][0] != v && fa[v][0] != u && u < v) e[++tot] = {u, v, w};
}
}
sort(e + 1, e + 1 + tot, cmp);
for(int i = 1; i <= tot; i++) {
int u = e[i].u, v = e[i].v, w = e[i].w;
int sum = d[u] + d[v] + w;
u = find(u); v = find(v);
while(u != v) {
if(dep[u] < dep[v]) swap(u, v);
ans[u] = sum - d[u];
f[u] = fa[u][0];
u = find(u);
}
}
for(int i = 2; i <= n; i++) printf("%d\n", ans[i]);
return 0;
}

浙公网安备 33010602011771号