[USACO09JAN]安全出行Safe Travel
最短路好题
话说还有最短路树这种操作,以前没听说过
题目要求我们求出从1到每个点不经过最短路的最后一条路的最短路径长度
所以我们可以建出一棵最短路树
考虑割掉一条边的最短路该怎么求
如果我们要求Ans[i]
那么就要找到一条非树边(因为能到达i的树边已经断了,只有非树边才能使图构成一个环)
那么\(Ans[i]=dis[u]+dis[v]+w(u,v)-dis[i]\)
其中u是i的子树里的点,v必须不是i子树里的点
这样u,v就可以更新从他们到LCA处的答案
这样我们可以把所有非树边按照\(dis[u]+dis[v]+w(u,v)\)排个序
就可以直接从前往后更新答案了
但是每个点只能更新一次(因为越早更新肯定越优)
所以使用并查集来将更新过的点合并
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 400005 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w= - 1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , m ;
struct E { int Nxt , to , Dis , From ; }edge[M<<1] , b[M<<1] ;
int hea[M] , num = 1 ;
int fir[M] , fnum ;
struct Node { int Id , Dis ; };
priority_queue< Node > q ;
bool vis[M] , Ist[M] ;
int Dis[M] , fa[M] , Ans[M] , f[M] , dep[M] ;
inline bool operator < (E a , E b) { return a.Dis < b.Dis ; }
inline bool operator < (Node a , Node b) { return a.Dis > b.Dis ; }
inline int Find(int x) { if(f[x] != x) f[x] = Find(f[x]) ; return f[x] ; }
inline void add_edge(int from , int to , int Dis) {
edge[++num].Nxt = hea[from] ; edge[num].to = to ; edge[num].From = from ;
edge[num].Dis = Dis ; hea[from] = num ;
}
inline void Insert_edge(int from , int to , int Dis) {
b[++fnum].Nxt = fir[from] ; b[fnum].to = to ; b[fnum].From = from ;
b[fnum].Dis = Dis ; fir[from] = fnum ;
}
inline void Dijkstra(int S) {
memset(Dis , 63 , sizeof(Dis)) ; Dis[S] = 0 ; q.push((Node){S , 0}) ;
while(!q.empty()) {
int u = q.top().Id ; q.pop() ;
if(vis[u]) continue ; vis[u] = true ;
for(int i = hea[u] ; i ; i = edge[i].Nxt) {
int v = edge[i].to ;
if(Dis[v] > Dis[u] + edge[i].Dis) {
Dis[v] = Dis[u] + edge[i].Dis ; fa[v] = u ;
if(!vis[v]) q.push((Node){v , Dis[v]}) ;
}
}
}
}
void Dfs(int u , int depth) {
dep[u] = depth ;
for(int i = hea[u] , v ; i ; i = edge[i].Nxt) {
v = edge[i].to ; if(fa[v] != u) continue ;
Ist[i] = Ist[i ^ 1] = true ; Dfs(v , depth + 1) ;
}
}
inline void Re_Build() {
for(int i = 2 , u , v ; i <= num ; i += 2) {
if(!Ist[i]) {
u = edge[i].From , v = edge[i].to ;
Insert_edge(u , v , Dis[u] + Dis[v] + edge[i].Dis) ;
}
}
}
inline void Solve() {
memset(Ans , -1 , sizeof(Ans)) ;
sort(b + 1 , b + fnum + 1) ;
for(int i = 1 ; i <= n ; i ++) f[i] = i ;
for(int i = 1 , u , v , x , y ; i <= fnum ; i ++) {
u = b[i].From , v = b[i].to ;
x = Find(u) , y = Find(v) ;
while(x != y) {
if(dep[x] < dep[y]) swap(x , y) ;
Ans[x] = b[i].Dis - Dis[x] ;
f[x] = Find(fa[x]) ; x = Find(x) ;
}
}
}
int main() {
n = read() ; m = read() ;
for(int i = 1 , u , v , w ; i <= m ; i ++) {
u = read() , v = read() , w = read() ;
add_edge(u , v , w) ; add_edge(v , u , w) ;
}
Dijkstra(1) ; Dfs(1 , 1) ; Re_Build() ; Solve() ;
for(int i = 2 ; i <= n ; i ++) printf("%d\n",Ans[i]) ;
return 0 ;
}