P r o b l e m \mathrm{Problem} Problem
S o l u t i o n \mathrm{Solution} Solution
首先这道题有一个显然的性质:
- 最短路径最多只经过一条非树边。
- 因为若有两条非树边 ( x , y ) (x,y) (x,y)和 ( u , v ) (u,v) (u,v),直接从 1 1 1 走到 y y y 显然不会使路径边长。
因此,我们对于每一条非树边都考虑其贡献。
-
情况1:非树边 ( u , v , w ) (u,v,w) (u,v,w)中, u u u 和 v v v 不存在祖先关系。
贡献为: d i s u + d i s v + w − d i s x dis_u+dis_v+w-dis_x disu+disv+w−disx
其中 x x x 表示最终你走到的那个点,显然 x x x 处于 u u u 到 v v v 的路径上。因此我们对整条路径更新贡献 d i s u + d i s v − w dis_u+dis_v-w disu+disv−w的最小值,最后减去每一个节点的深度即可。 -
情况 2 2 2:边 ( u , v , w ) (u,v,w) (u,v,w)中, u u u 和 v v v 存在祖先关系。
显然贡献也是: d i s u + d i s v + w − d i s x dis_u+dis_v+w-dis_x disu+disv+w−disx
所以一起处理即可。
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5000;
const int M = 2e5;
int n, m;
int dep[N], fa[N], res[N];
pair< pair<int,int>, pair<int,int> > E[M];
vector < pair< int, pair<int, int> > > a[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (!isdigit(c)) w |= c == '-', c = getchar();
while (isdigit(c)) s = s*10+c-48, c = getchar();
return w ? -s : s;
}
void Dfs1(int x, int f)
{
fa[x] = f;
for (int i=0;i<a[x].size();++i)
{
int y = a[x][i].first;
int v = a[x][i].second.first;
int t = a[x][i].second.second;
if (y == fa[x] || t == 0) continue;
dep[y] = dep[x] + v;
Dfs1(y, x);
}
return;
}
int main(void)
{
freopen("shortest.in","r",stdin);
freopen("shortest.out","w",stdout);
n = read(), m = read();
for (int i=1;i<=m;++i)
{
int x = read(), y = read();
int v = read(), t = read();
a[x].push_back({y, {v, t}});
a[y].push_back({x, {v, t}});
E[i] = {{x, y}, {v, t}};
}
Dfs1(1, 0);
for (int i=1;i<=n;++i) res[i] = 1e9;
for (int i=1;i<=m;++i)
{
int x = E[i].first.first;
int y = E[i].first.second;
int w = E[i].second.first;
int u = x, v = y;
if (E[i].second.second) continue;
while (x != y)
{
if (dep[x] < dep[y]) swap(x, y);
res[x] = min(res[x], dep[u] + dep[v] + w - dep[x]);
x = fa[x];
}
}
for (int i=2;i<=n;++i) printf("%d ", res[i] > 1e8 ? -1 : res[i]);
return 0;
}