[BZOJ1977]严格次小生成树-kruskal+倍增维护

来源:互联网 发布:营口港大数据平台 编辑:程序博客网 时间:2024/06/08 15:40

写在前面

一定要三思而后submit啊hhhhh


题目

这貌似是一道权限题…但是还是放一个传送门吧qwq
BZOJ1977传送门

题面

小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。 正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:
(value(e) 表示边 e的权值) 这里写图片描述这下小C蒙了,他找到了你,希望你帮他解决这个问题。

输入及输出

Input:第一行包含两个整数N和M,表示无向图的点数与边数。接下来M行,每行3个数x,y,z表示,点x和点y之间有一条边,权值为z。
Output:包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

样例

(其实这个样例还可以)

simple IN:
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
simple OUT:
11


解法

本题就是要求一个严格的次小生成树,严格就是说,这个次小生成树的边权和大于(不能取等)最小生成树

一般的次小生成树就是,枚举每条边e( u , v ),找u->v路径中权值最大的那条边,用替换之后的边权和更新答案。

树上两点路径维护,倍增肯定是少不了的,关键是要维护什么值。
因为这个次小是严格次小,因此如果length( e ) 和 length( p )相等明显不符合题意。为了解决这种情况,我们需要找一条次大的边p1,并用e去替换p1,这样才能保证是严格次小。
因此,倍增维护需要维护三个值:fa , 最大值 , 次大值。

注意这个次大值是不能和最大值相等的,维护的时候有一点小技巧,想清楚了再写不容易错。细节看代码。


下面是自带大常数的代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std ;int N , M , tp , head[100005] ;long long sum , ans ;struct Edge{    int u , v , val ;    bool ontree ;    bool operator < ( const Edge &A ) const {        return val < A.val ;    }}e[300005] ;struct Path{    int pre , to , val ;}p[100005<<2] ;class Union_set{    public :        void init(){            for( int i = 0 ; i <= N ; i ++ )                fa[i] = i ;        }        int find( int x ){            if( fa[x] == x ) return x ;            return fa[x] = find( fa[x] ) ;        }        bool Union( int x , int y ){            int F_x = find( x ) ;            int F_y = find( y ) ;            if( F_x == F_y ) return false ;            fa[F_x] = F_y ;            return true ;        }    private :        int fa[100005] ;}U;void In( int t1 , int t2 , int t3 ){    p[++tp].pre = head[t1] ;    p[ head[t1] = tp ].to = t2 ;    p[tp].val = t3 ;}void kruskal(){    sort( e + 1 , e + M + 1 ) ;    U.init() ;    for( int i = 1 , cnt = 0 ; i <= M ; i ++ )        if( U.Union( e[i].u , e[i].v ) ){            In( e[i].u , e[i].v , e[i].val ) ;            In( e[i].v , e[i].u , e[i].val ) ;            e[i].ontree = true ;            sum += e[i].val ;            //printf( "(kruskal ) %d connec with %d  val : %d\n" , e[i].u , e[i].v , e[i].val ) ;            if( ( ++cnt ) == N - 1 ) break ;        }    //printf( "\n" ) ;    ans = 1LL * 1e9 * 100005 ;//初始化无限大}int fa[100005][17] , maxv[100005][17] , maxblv[100005][17] , dep[100005] ;void dfs( int u ){    for( int i = head[u] ; i ; i = p[i].pre ){        int v = p[i].to ;        if( v == fa[u][0] ) continue ;        fa[v][0] = u ; dep[v] = dep[u] + 1 ;        maxv[v][0] = p[i].val ;        maxblv[v][0] = -1 ;        dfs( v ) ;    }}void cntmul(){    for( int i = 1 ; i <= 16 ; i ++ ){        for( int j = 1 ; j <= N ; j ++ ){            fa[j][i] = fa[ fa[j][i-1] ][i-1] ;            if( maxv[j][i-1] != maxv[ fa[j][i-1] ][i-1] ){//倍增维护细节                maxv[j][i] = max( maxv[j][i-1] , maxv[j][i-1] ][i-1] ) ;                if( maxv[j][i-1] > maxv[ fa[j][i-1] ][i-1] )                     maxblv[j][i] = max( maxblv[j][i-1] , maxv[ fa[j][i-1] ][i-1] ) ;                else maxblv[j][i] = max( maxblv[ fa[j][i-1] ][i-1] , maxv[j][i-1] ) ;            } else {                maxv[j][i] = maxv[j][i-1] ;                maxblv[j][i] = max( maxblv[j][i-1] , maxblv[ fa[j][i-1] ][i-1] );            }        }    }}template <typenam, T >void getn( T &maxr , T &maxblr , const T &mv , const T &mblv ){    //用链的最大值mv,次大值mblv,更新当前已经求得的最大值maxr,次大值maxblr。    if( maxr < mv ){        maxblr = ( mblv > maxr ? mblv : maxr ) ;        maxr = mv ;    } else if( maxr == mv ) maxblr = max( maxblr , mblv ) ;      else                  maxblr = max( maxblr , mv ) ;}int getLca( int u , int v , int Eval ){    if( dep[u] < dep[v] ) swap( u , v ) ;    int t = dep[u] - dep[v] , x = 0 , maxr = -1 , maxblr = -1 ;    while( t ){        if( t&1 ){            getn( maxr , maxblr , maxv[u][x] , maxblv[u][x] ) ;            u = fa[u][x] ;        }        t >>= 1 ; x ++ ;    }    if( u == v )         return Eval - ( Eval == maxr ? maxblr : maxr ) ;    for( int i = 16 ; i >= 0 ; i -- ){        if( fa[u][i] != fa[v][i] ){            getn( maxr , maxblr , maxv[u][i] , maxblv[u][i] ) ;            getn( maxr , maxblr , maxv[v][i] , maxblv[v][i] ) ;            u = fa[u][i] ; v = fa[v][i] ;        }    }    getn( maxr , maxblr , maxv[u][0] , maxblv[u][0] ) ;    getn( maxr , maxblr , maxv[v][0] , maxblv[v][0] ) ;    return Eval - ( Eval == maxr ? maxblr : maxr ) ;}void solve(){    kruskal() ;    fa[1][0] = 1 ; dfs( 1 ) ;    cntmul() ;    for( int i = 1 ; i <= M ; i ++ ){        if( e[i].ontree ) continue ;        ans = min( ans , sum + getLca( e[i].u , e[i].v , e[i].val ) ) ;    }    printf( "%lld" , ans ) ;}int main(){    scanf( "%d%d" , &N , &M ) ;    for( int i = 1 ; i <= M ; i ++ )        scanf( "%d%d%d" , &e[i].u , &e[i].v , &e[i].val ) ;    solve() ;}
阅读全文
0 0