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
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
HINT
數據中無向圖無自環; 50% 的數據N≤2 000 M≤3 000; 80% 的數據N≤50 000 M≤100 000; 100% 的數據N≤100 000 M≤300 000 ,邊權值非負且不超過 10^9 。
解題報告:樹上倍增+最小生成樹
#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 ;
}