1. 程式人生 > 資訊 >乘聯會祕書長崔東樹:2021 年 1-8 月中國佔世界新能源車 50%

乘聯會祕書長崔東樹:2021 年 1-8 月中國佔世界新能源車 50%

luoguP4180 [BJWC2010]嚴格次小生成樹

題目大意:

給出一張無向圖,求出這個圖的嚴格次小生成樹。

解法:

嚴格次小生成樹滿足的條件之一是當且僅當只有一條邊和最小生成樹不同時才有可能是嚴格次小生成樹。

所以我們先求出最小生成樹,之後列舉每條不在最小生成樹上的邊,把這條邊加入生成樹中,這樣子原樹就出現了一個環,我們找出這個環中除了當前邊最大的邊權,這樣我們的答案就比最小生成樹多了 當前邊權減這個環上第二大的邊權 這個值的答案,最後對這些所有的答案取最小值就可以了。

其中,對於求環上的第二大的邊權,我們可以用樹剖做,然後求出每次列舉的邊的兩個端點在樹上的路徑上的邊權的最大值,然後在用當前邊權減去這個最大值就是當前邊加入生成樹的影響。

(瓶頸)複雜度: \(O(m \log^2 n )\)

Code:

#include <iostream>
#include <algorithm>
using namespace std ;

#define int long long
const int INF = 0x3f3f3f3f3f3f3f3f ;

struct Edge
{
	int u , v , w , id ;
	friend bool operator < ( Edge a , Edge b )
	{
		return a .w < b .w ;
	}
} ed[300005] ;

int n , m , clr[100005] , ans , dt , res = INF , w[100005] , t[800005] , in[800005] ;
int nxt[200005] , to[200005] , head[100005] , len[200005] ;
int dep[100005] , fa[100005] , son[100005] , s[100005] , id[100005] , wt[100005] , top[100005] ;

int find ( int x )
{
	if ( x == clr [ x ] )
		return x ;
	return clr [ x ] = find ( clr [ x ] ) ;
}

int cnt = 0 ;
void insert ( int u , int v , int w )
{
	nxt [ ++ cnt ] = head [ u ] ;
	to [ cnt ] = v ;
	len [ cnt ] = w ;
	head [ u ] = cnt ;
}

void dfs1 ( int x , int la , int d )
{
	dep [ x ] = d ;
	fa [ x ] = la ;
	s [ x ] = 1 ;
	int axx = 0 ;
	for ( int i = head [ x ] ; i ; i = nxt [ i ] )
	{
		int y = to [ i ] ;
		if ( y == la )
			continue ;
		w [ y ] = len [ i ] ;
		dfs1 ( y , x , d + 1 ) ;
		s [ x ] += s [ y ] ;
		if ( s [ y ] > axx )
			son [ x ] = y , axx = s [ y ] ;
	}
}

void dfs2 ( int x , int topf )
{
	top [ x ] = topf ;
	id [ x ] = ++ dt ;
	wt [ dt ] = w [ x ] ;
	if ( ! son [ x ] )
		return ;
	dfs2 ( son [ x ] , topf ) ;
	for ( int i = head [ x ] ; i ; i = nxt [ i ] )
	{
		int y = to [ i ] ;
		if ( y == fa [ x ] || y == son [ x ] )
			continue ;
		dfs2 ( y , y ) ;
	}
}

void pushup ( int k )
{
	if ( t [ k << 1 ] > t [ k << 1 | 1 ] )
	{
		t [ k ] = t [ k << 1 ] ;
		in [ k ] = t [ k << 1 | 1 ] ;
	}
	else if ( t [ k << 1 ] < t [ k << 1 | 1 ] )
	{
		t [ k ] = t [ k << 1 | 1 ] ;
		in [ k ] = t [ k << 1 ] ;
	}
	else
	{
		in [ k ] = max ( in [ k << 1 ] , in [ k << 1 | 1 ] ) ;
	}
}

void build ( int k , int l , int r )
{
	if ( l == r )
	{
		t [ k ] = wt [ l ] ;
		in [ k ] = 0 ;
		return ;
	}
	int mid = ( l + r ) >> 1 ;
	build ( k << 1 , l , mid ) ;
	build ( k << 1 | 1 , mid + 1 , r ) ;
	pushup ( k ) ;
}

int query ( int k , int l , int r , int x , int y , int z )
{
	if ( x <= l && r <= y )
	{
		if ( t [ k ] == z )
			return in [ k ] ;
		return t [ k ] ;
	}
	int mid = ( l + r ) >> 1 , res = 0 ;
	if ( x <= mid )
		res = query ( k << 1 , l , mid , x , y , z ) ;
	if ( y > mid )
		res = max ( res , query ( k << 1 | 1 , mid + 1 , r , x , y , z ) ) ;
	return res ;
}

int find ( int x , int y , int z )
{
	int res = 0 ;
	while ( top [ x ] != top [ y ] )
	{
		if ( dep [ top [ x ] ] < dep [ top [ y ] ] )
			swap ( x , y ) ;
		res = max ( res , query ( 1 , 1 , n , id [ top [ x ] ] , id [ x ] , z ) ) ;
		x = fa [ top [ x ] ] ;
	}
	if ( dep [ x ] > dep [ y ] )
		swap ( x , y ) ;
	if ( x != y )
		res = max ( res , query ( 1 , 1 , n , id [ x ] + 1 , id [ y ] , z ) ) ;
	return res ;
}

signed main ( )
{
	cin >> n >> m ;
	for ( int i = 1 ; i <= n ; ++ i )
		clr [ i ] = i ;
	for ( int i = 1 ; i <= m ; ++ i )
		cin >> ed [ i ] .u >> ed [ i ] .v >> ed [ i ] .w ;
	sort ( ed + 1 , ed + 1 + m ) ;
	for ( int i = 1 ; i <= m ; ++ i )
	{
		int u = ed [ i ] .u , v = ed [ i ] .v , a = find ( u ) , b = find ( v ) ;
		if ( a != b )
		{
			clr [ a ] = b ;
			ed [ i ] .id = 1 ;
			insert ( u , v , ed [ i ] .w ) ;
			insert ( v , u , ed [ i ] .w ) ;
			ans += ed [ i ] .w ;
		}
	}
	dfs1 ( 1 , 0 , 1 ) ;
	dfs2 ( 1 , 1 ) ;
	build ( 1 , 1 , n ) ;
	for ( int i = 1 ; i <= m ; ++ i )
	{
		if ( ed [ i ] .id )
			continue ;
		int u = ed [ i ] .u , v = ed [ i ] .v , w = ed [ i ] .w ;
		int z = find ( u , v , w ) ;
		if ( w != z && w - z < res )
			res = w - z ;
	}
	cout << res + ans << endl ;
	return 0 ;
}

完結撒花!!!