题面
解法
最短路树……emmmm,从未听说过……
- 因为题目已经保证从 1 1 到其他所有点的最短路是唯一的,所以我们不妨把这些边全都记下来,然后就可以发现这些边共同构成一棵树
- 首先考虑为什么这是一棵树。显然,我们只要说明这些边不会构成环即可。如果出现环,那么说明从1到环上的点一定由至少2条最短路,和题目描述出现矛盾,所以这一定是一棵树
- 然后我们考虑,一条不在树上的边会对哪些点的答案造成影响。显然,一定是树上 x x 到路径上的所有点(不包括这两个点的 lca l c a )。假设路径上有一点 i i ,那么对的影响就是 dis[x]+dis[y]−dis[i]+v d i s [ x ] + d i s [ y ] − d i s [ i ] + v 。因为 dis[i] d i s [ i ] 对于 i i 是一个固定的值,所以我们只要求出每一个点对它造成影响的边中 dis[x]+dis[y]+v d i s [ x ] + d i s [ y ] + v 最小的那一个就可以了
- 这个显然可以用树剖+线段树维护吧,时间复杂度 O((m+n)logn+mlog2n) O ( ( m + n ) log n + m log 2 n ) ,可以通过此题
我太懒了,不想写树剖- 但是我们可以发现,如果对于每一条不在树上的边 (x,y,v) ( x , y , v ) 按照 dis[x]+dis[y]+v d i s [ x ] + d i s [ y ] + v 从小到大排序,那么每一个点的答案一定会被第一次能影响到它的边更新,不必每一次都更新答案
- 那么,我们就可以使用并查集来维护这一个过程,每一次在更新的时候可以直接跳过已经求出答案的部分
- 时间复杂度: O((m+n)logn+mα(n)) O ( ( m + n ) log n + m α ( n ) )
代码
#include <bits/stdc++.h>
#define int long long
#define PI pair <int, int>
#define mp make_pair
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
int next, num, v;
} e[N * 5];
struct Node {
int x, y, val;
bool operator < (const Node &a) const {
return val < a.val;
}
} a[N * 5];
int n, m, cnt, p[N], fa[N], ans[N], dis[N], used[N];
void add(int x, int y, int v) {
e[++cnt] = (Edge) {e[x].next, y, v};
e[x].next = cnt;
}
void dijkstra(int s) {
for (int i = 1; i <= n; i++)
dis[i] = INT_MAX, used[i] = 0;
priority_queue <PI, vector <PI>, greater <PI> > h;
dis[s] = 0; h.push(mp(dis[s], s));
while (!h.empty()) {
PI tmp = h.top(); h.pop();
int x = tmp.second;
if (used[x]) continue; used[x] = 1;
for (int p = e[x].next; p; p = e[p].next) {
int k = e[p].num, v = e[p].v;
if (dis[k] > dis[x] + v) {
dis[k] = dis[x] + v, fa[k] = x;
h.push(mp(dis[k], k));
}
}
}
}
int Find(int x) {
if (p[x] == x) return x;
return p[x] = Find(p[x]);
}
main() {
read(n), read(m); cnt = n;
for (int i = 1; i <= m; i++) {
int x, y, v; read(x), read(y), read(v);
add(x, y, v);
}
dijkstra(1); int tot = 0;
for (int i = 1; i <= n; i++)
for (int p = e[i].next; p; p = e[p].next) {
int k = e[p].num, v = e[p].v;
a[++tot] = (Node) {i, k, dis[i] + dis[k] + v};
}
sort(a + 1, a + tot + 1);
for (int i = 1; i <= n; i++) p[i] = i;
memset(ans, -1, sizeof(ans));
for (int i = 1; i <= tot; i++) {
int x = a[i].x, y = a[i].y, v = a[i].val;
if (x == fa[y] || y == fa[x]) continue;
x = Find(x), y = Find(y);
while (x != y) {
if (dis[x] < dis[y]) swap(x, y);
ans[x] = v - dis[x];
p[x] = Find(fa[x]), x = Find(x);
}
}
for (int i = 2; i <= n; i++) cout << ans[i] << "\n";
return 0;
}