[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 ;
}

posted @ 2018-09-09 07:52  beretty  阅读(214)  评论(1编辑  收藏  举报