[BZOJ1576]安全路径travel-最短路径树+并查集

来源:互联网 发布:装修公司源码 编辑:程序博客网 时间:2024/06/15 15:27

说在前面

= =本来这个题被放在数据机构刷题提单里,然后还备注着说是”最短路径树+树链剖分+线段树维护”,然而实际上好像并不需要,只需要一个”最短路径树+并查集维护”就可以了。


题目

先给链接:BZOJ1576传送门(这是一道权限题XD)

题意


简单的说,就是求出1号点到每个点路径(该路径不能经过最短路的最后一条边)的最短距离是多少。

题解

首先,这道题相当于是让我们求出不走某一条边的最短路
我们先搞出最短路径树,这一步用dijkstra堆优化
搞出来之后,我们假设根节点是1号点,那么对于不在最短路径树上的某一条边u->v(本来是无向边,把无向边看成两条有向边即可),对于1号点到u路径上的任意一点x,那么x的一条其他路径就是1->v->u->x
设dist[i]表示1号点(根节点)到i号点的距离,这条”其他路径”的长度就可以表示成

dist[u]+dist[v]+len(u>v)dist[x]

注意到,对于每个这样的节点x,dist[x]一定是固定的,并且对于每一条边,都拥有自己的dist[u]+dist[v]+len(u->v),那我们只需要用每条边依次去更新每个点的ans就好了。

Emmmmmm……

注意到这个复杂度是十分高的,我们需要对这个算法进行优化
我们可以发现,一个点,假设它是x,可能会被更新很多次,但是实际上最优的那个更新,一定是由min( dist[u]+dist[v]+len(u->v) )-dist[x]得到的。我们只需要对所有的边按照dist[u]+dist[v]+len(u->v)从小到大排个序,那么当一个点被更新的时候,一定就是那个最优值,即之后的对该点的更新都不如之前的优,每个点只更新一次就好了。这一步我们可以用并查集进行维护。

然后就可以愉快的AC啦qwq


下面是自带大常数的代码

fa数组是最短路径树的fa
而b数组相当于是并查集的fa
vis是为了在堆优化的时候防止重复更新
其他的数组应该不难懂= =

/**************************************************************    Problem: 1576    User: Izumihanako    Language: C++    Result: Accepted    Time:1320 ms    Memory:9944 kb****************************************************************/#include <queue>#include <cstdio>#include <cstring>#include <algorithm>#define pr pair<int,int>#define fi first#define se secondusing namespace std ;int N , M , tp , head[100005] , b[100005] , ans[100005] ;int dist[100005] , vis[100005] , fa[100005] , dep[100005] ;struct Path{    int fr , to , len , pre ;    bool operator < ( const Path &A ) const {        return dist[fr] + dist[to] + len < dist[A.fr] + dist[A.to] + A.len ;    }}p[400005] ;void In ( int t1 , int t2 , int t3 ){    p[++tp].pre = head[t1] ;    p[ head[t1] = tp ].to = t2 ;    p[tp].len = t3 ;    p[tp].fr = t1 ;}priority_queue< pr , vector<pr> , greater<pr> > q ;void Dijk(){    memset( dist , 0x3f , sizeof( dist ) ) ;    pr S = make_pair( 0 , 1 ) ; q.push( S ) ;    fa[1] = 1 ; dep[1] = dist[1] = 0 ;    while( !q.empty() ){        pr u = q.top() ; q.pop() ;        if( vis[u.se] ) continue ;        vis[u.se] = 1 ;        for( int i = head[u.se] ; i ; i = p[i].pre ){            int v = p[i].to ;            if( dist[v] > dist[u.se] + p[i].len ){                dist[v] = dist[u.se] + p[i].len ;                fa[v] = u.se ; dep[v] = dep[u.se] + 1 ;                q.push( make_pair( dist[v] , v ) ) ;            }        }    }}int find( int x ){    if( b[x] == x ) return x ;    return b[x] = find( b[x] ) ;}void solve(){    for( int i = 1 ; i <= N ; i ++ ) b[i] = i ;    memset( ans , -1 , sizeof( ans ) ) ;    for( int i = 1 ; i <= tp ; i ++ ){        if( p[i].fr == fa[p[i].to] || p[i].to == fa[p[i].fr] ) continue ;       int u = find( p[i].fr ), v = find( p[i].to ), val = p[i].len + dist[p[i].fr] + dist[p[i].to] ;        while( v != u ){            if( dep[v] > dep[u] ) swap( u , v ) ;            ans[u] = val - dist[u] ;            int F_u = find( u ) , FF_u = find( fa[u] ) ;            b[F_u] = b[FF_u] ;            u = b[FF_u] ;        }    }}int main(){    scanf( "%d%d" , &N , &M ) ;    for( int i = 1 , t1 , t2 , t3 ; i <= M ; i ++ ){        scanf( "%d%d%d" , &t1 , &t2 , &t3 ) ;        In( t1 , t2 , t3 ) ;        In( t2 , t1 , t3 ) ;    }    Dijk() ;    sort( p + 1 , p + tp + 1 ) ;    solve() ;    for( int i = 2 ; i <= N ; i ++ )        printf( "%d\n" , ans[i] ) ;}
阅读全文
0 0