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