BZOJ1977 [BeiJing2010組隊] [次小生成樹 Tree]


Description

小 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

包含一行,僅一個數,表示嚴格次小生成樹的邊權和。(數據保證必定存在嚴格次小生成樹)

Sample Input

5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6

Sample Output

11

HINT

數據中無向圖無自環; 50% 的數據N≤2 000 M≤3 000; 80% 的數據N≤50 000 M≤100 000; 100% 的數據N≤100 000 M≤300 000 ,邊權值非負且不超過 10^9 。

解題報告:樹上倍增+最小生成樹

這道題知識點簡單,但是考察代碼能力和細節處理。我採用的是求LCA的方法(常數大)思路就是先求出最小生成樹,然後用不在樹上的邊來代替樹上的邊,然後問題就轉化爲了求出樹上兩點間最大邊權與次大邊權,在處理合並的時候有一些細節要處理(具體看代碼)

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7 ;
const int P = 18 ;
struct Edge{
    int u , v , w ;
    Edge ( int u=0 , int v=0 , int w=0 ) : u(u) , v(v) , w(w) {}
};
Edge e [N<<2];
bool cmp ( Edge x , Edge y ) {
    return x.w < y.w ;
}
 
vector <Edge> edges;
vector <int> G [N] ;
 
inline int readin(){
	int x=0 , f=1 ; char ch = getchar ();
	while ( !isdigit ( ch ) ){
		if ( ch == '-' ) f = -1 ;
		ch = getchar ();
	}
	while ( isdigit ( ch ) ){
		x = x * 10 + ch - '0' ;
		ch = getchar();
	}
	return x * f ;
}

void addeage ( int u , int v , int w ) {
    edges.push_back ( Edge ( u , v , w ) ) ;
    int tot = edges.size () ;
    G [u].push_back ( tot - 1 ) ;
}
 
int fa [N] [P] , fm [N] [P] , sm [N] [P] , dep [N] , vis [N<<2] , ans;
long long ret = 0;
 
int dfs ( int u ){
    for ( int i = 1 ; i < P ; ++ i ) {
        fa [u] [i] = fa [ fa [u] [i-1] ] [i-1] ;
        int t1 = fm [u] [i-1] , t2 = fm [ fa [u] [i-1] ] [i-1] ;
        fm [u] [i] = max ( t1 , t2 ) ;
        sm [u] [i] = max ( sm [u] [i-1] , sm [ fa [u] [i-1] ] [i-1] ) ;
        if ( t1 != t2 ) sm [u] [i] = max ( sm [u] [i] , min ( t1 , t2 ) );      
    }
    for ( int i = 0 ; i < G [u].size () ; ++ i ) {
        Edge e = edges [ G [u] [i] ] ;
        if ( e.v == fa [u][0] ) continue ;
        dep [e.v] = dep [u] + 1 ;
        fa [e.v][0] = u ;
        fm [e.v][0] = e.w ; 
        dfs ( e.v );
    }
}
 
int LCA ( int u , int v ) {
    if ( dep [u] < dep [v] ) swap ( u , v ) ;
    int h = dep [u] - dep [v] ;
//  printf ( "h: %d \n" , h );
    for ( int i = P ; i >= 0 && h ; -- i ) if ( h & (1<<i) ){
        u = fa [u][i] ;
//      printf ( " u = %d\n" , u ) ;
    }
    if ( u == v ) return u ;
    for ( int i = P-1 ; i >= 0 ; -- i ) {
        if ( fa [u] [i] != fa [v] [i] ) {
            u = fa [u] [i] ;
            v = fa [v] [i] ;
//          printf ( "%d %d %d\n" , i , u , v ) ;
        }
    }
    return fa [u] [0] ;
}
 
void query ( int u , int x , int v ) {
    int t = dep [u] - dep [x];
    int fmx , smx ;
    fmx = smx = 0 ;
//  printf ( "----u = %d  x = %d----\n" , u , x ) ;
    for ( int i = P ; i >= 0 ; -- i ) if ( t & ( 1<<i ) ) {
        if ( fm [u] [i] > fmx ){
            smx = fmx ;
            fmx = fm [u] [i] ;
        }else if ( fm [u] [i] != fmx ) smx = max ( smx , fm [u] [i] ); 
        //
        smx = max ( smx , sm [u] [i] ) ;
        u = fa [u] [i] ;  
    }
//  printf ( "fmx = %d , smx = %d\n" , fmx , smx ) ;
    if ( v == fmx ) ans = min ( ans , v - smx ) ;
    else if ( fmx < v ) ans = min ( ans , v - fmx ) ;
}

void work ( int i ){
    int u = e [i].u , v = e [i].v , w = e [i].w ;
    int x = LCA ( u , v ) ;
    query ( u , x , w ) ;
    query ( v , x , w ) ;
}
 
int fat [N] ;
 
int find ( int u ) {
    return u == fat [u] ? u : fat [u] = find ( fat [u] ) ;
}
 
void Kruskal ( int n , int m ) {
    int tot = 0 ;
    for ( int i = 1 ; i <= n ; ++ i ) fat [i] = i ;
    sort ( e + 1 , e + 1 + m , cmp ) ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        int u = e [i].u , v = e [i].v , w = e [i].w ;
        int fu = find ( u ) ;
        int fv = find ( v ) ;
        if ( fu != fv ) {
            fat [fu] = fv ;
            tot ++ ;
            ret += w ;
            vis [i]  = 1;
            addeage ( u , v , w ) ;
            addeage ( v , u , w ) ;
    //      printf ( "%d %d %d\n" , u , v , w ) ;
            if ( tot == n - 1 ) break;
        } 
    }
}
 
int main () {
    int n , m ;
//    scanf ( "%d%d", &n ,&m );
	n = readin() , m = readin();
	for ( int i = 1 ; i <= m ; ++ i ) {
		e [i].u = readin() , e [i].v = readin() , e [i].w = readin();
//        scanf ( "%d%d%d" , & e [i].u , & e [i].v , & e [i].w ) ;
    }
    ret = 0 , ans = 1e9 + 10 ;
    Kruskal ( n , m ) ;
    dep [1] = 1;
    dfs ( 1 ) ;
    int tota = 0;
    for ( int i = 1 ; i <= m ; ++ i ) if ( ! vis [i] ){
        work ( i ) ;
        tota ++;
    }
    if ( !tota ) ans = 0 ;
    printf ( "%lld" , (long long)ret + ans ) ;
 
    return 0 ;
}

























發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章