• 最小割树小记


    概要

    最小割树是解决无向图上任意两点间最小割问题的工具。其核心思想为分治。

    现在有一个图 (G=(V,E)) ,可以这样求得它的最小割树:

    选取两个点 (u,v) ,求得这两个点之间的最小割。这个最小割将原图分为两部分 (G_s)(G_t) 。任意 (xin G_s)(y in G_y) 之间的最小割 至少(u,v) 之间的最小割。

    那么可以对于 (G_s)(G_y) 分别递归。最后两个点之间的最小割就是在这个递归树上路径的最大值。

    具体地,对于每一次递归,可以在求得最小割之后在新图上在 (u,v) 之间连一条权值为最小割的边。那么最后两个点之间的最小割就是树上路径上的最大边权。

    如果使用 Dinic 求最小割的话,时间复杂度是 (O(n^3m)) 的。但一般认为是 (O(能过))

    模板

    luogu4897

    #include <bits/stdc++.h>
    using namespace std;
    
    const int Maxn = 510;
    const int Maxm = 1510;
    const int INF = 2147483647;
    const int MaxLog = 20;
    int n, m, Q[ Maxn ], P[ Maxn ], q;
    int Start[ Maxn ], Cur[ Maxn ], Next[ Maxm << 1 ], To[ Maxm << 1 ], Flow[ Maxm << 1 ], NowFlow[ Maxm << 1 ], Used;
    int Start_[ Maxn ], Next_[ Maxn << 1 ], To_[ Maxn << 1 ], Val_[ Maxn << 1 ], Used_, D[ Maxn ][ MaxLog ][ 2 ], Deep_[ Maxn ];
    int Deep[ Maxn ], Dis[ Maxn ], L, R, Queue[ Maxn ];
    
    inline void AddEdge( int x, int y, int z ) {
    	++Used;
    	Next[ Used ] = Start[ x ];
    	To[ Used ] = y;
    	Flow[ Used ] = z;
    	Start[ x ] = Used;
    	return;
    }
    
    inline void AddEdge_( int x, int y, int z ) {
    	++Used_;
    	Next_[ Used_ ] = Start_[ x ];
    	To_[ Used_ ] = y;
    	Val_[ Used_ ] = z;
    	Start_[ x ] = Used_;
    	return;
    }
    
    bool Bfs( int S, int T ) {
    	memset( Deep, 0, sizeof( Deep ) );
    	memset( Dis, 0, sizeof( Dis ) );
    	Deep[ S ] = 1;
    	L = R = 0;
    	Queue[ ++R ] = S;
    	while( L < R ) {
    		int u = Queue[ ++L ];
    		for( int t = Start[ u ]; t != -1; t = Next[ t ] ) {
    			int v = To[ t ];
    			if( Deep[ v ] ) continue;
    			if( NowFlow[ t ] <= 0 ) continue;
    			Deep[ v ] = Deep[ u ] + 1;
    			Queue[ ++R ] = v;
    		}
    	}
    	return Deep[ T ];
    }
    
    int Dfs( int u, int Rest, int T ) {
    	if( u == T || Rest <= 0 ) return Rest;
    	int Ans = 0;
    	for( int &t = Cur[ u ]; t != -1; t = Next[ t ] ) {
    		int v = To[ t ];
    		if( Deep[ v ] != Deep[ u ] + 1 ) continue;
    		if( NowFlow[ t ] <= 0 ) continue;
    		int d = Dfs( v, min( Rest, NowFlow[ t ] ), T );
    		NowFlow[ t ] -= d; NowFlow[ t ^ 1 ] += d;
    		Ans += d; Rest -= d;
    		if( !Rest ) break;
    	}
    	return Ans;
    }
    
    int Dinic( int S, int T ) {
    	int Ans = 0;
    	memcpy( NowFlow, Flow, sizeof( Flow ) );
    	while( Bfs( S, T ) ) {
    		memcpy( Cur, Start, sizeof( Start ) );
    		int d = Dfs( S, INF, T );
    		while( d ) {
    			Ans += d;
    			d = Dfs( S, INF, T );
    		}
    	}
    	return Ans;
    }
    
    void Build( int Left, int Right ) {
    	if( Left >= Right ) return;
    	int t = Dinic( Q[ Left ], Q[ Left + 1 ] );
    	AddEdge_( Q[ Left ], Q[ Left + 1 ], t );
    	Bfs( Q[ Left ], n + 1 );
    	t =  Left - 1;
    	for( int i = Left; i <= Right; ++i ) 
    		if( Deep[ Q[ i ] ] )
    			P[ ++t ] = Q[ i ];
    	int Cut = t;
    	for( int i = Left; i <= Right; ++i ) 
    		if( !Deep[ Q[ i ] ] )
    			P[ ++t ] = Q[ i ];
    	for( int i = Left; i <= Right; ++i ) 
    		Q[ i ] = P[ i ];
    	Build( Left, Cut );
    	Build( Cut + 1, Right );
    	return;
    }
    
    void Build_( int u, int Fa, int c ) {
    	D[ u ][ 0 ][ 0 ] = Fa;
    	for( int i = 1; i < MaxLog; ++i ) D[ u ][ i ][ 0 ] = D[ D[ u ][ i - 1 ][ 0 ] ][ i - 1 ][ 0 ];
    	D[ u ][ 0 ][ 1 ] = c;
    	for( int i = 1; i < MaxLog; ++i ) D[ u ][ i ][ 1 ] = min( D[ u ][ i - 1 ][ 1 ], D[ D[ u ][ i - 1 ][ 0 ] ][ i - 1 ][ 1 ] );
    	Deep_[ u ] = Deep_[ Fa ] + 1;
    	for( int t = Start_[ u ]; t; t = Next_[ t ] ) {
    		int v = To_[ t ];
    		if( v == Fa ) continue;
    		Build_( v, u, Val_[ t ] );
    	}
    	return;
    }
    
    int Query( int x, int y ) {
    	int Ans = INF;
    	if( Deep_[ x ] < Deep_[ y ] ) swap( x, y );
    	for( int i = MaxLog - 1; i >= 0; --i )
    		if( Deep_[ D[ x ][ i ][ 0 ] ] >= Deep_[ y ] ) {
    			Ans = min( Ans, D[ x ][ i ][ 1 ] );
    			x = D[ x ][ i ][ 0 ];
    		}
    	if( x == y ) return Ans;
    	for( int i = MaxLog - 1; i >= 0; --i ) 
    		if( D[ x ][ i ][ 0 ] != D[ y ][ i ][ 0 ] ) {
    			Ans = min( Ans, D[ x ][ i ][ 1 ] );
    			Ans = min( Ans, D[ y ][ i ][ 1 ] );
    			x = D[ x ][ i ][ 0 ];
    			y = D[ y ][ i ][ 0 ];
    		}
    	Ans = min( Ans, D[ x ][ 0 ][ 1 ] );
    	Ans = min( Ans, D[ y ][ 0 ][ 1 ] );
    	return Ans;
    }
    
    int main() {
    	Used = -1;
    	memset( Start, 255, sizeof( Start ) );
    	scanf( "%d%d", &n, &m );
    	for( int i = 1; i <= m; ++i ) {
    		int x, y, z;
    		scanf( "%d%d%d", &x, &y, &z );
    		AddEdge( x, y, z );
    		AddEdge( y, x, z );
    	}
    	for( int i = 0; i <= n; ++i ) Q[ i ] = i;
    	Build( 0, n );
    	Build_( 0, 0, INF );
    	scanf( "%d", &q );
    	for( int i = 1; i <= q; ++i ) {
    		int x, y;
    		scanf( "%d%d", &x, &y );
    		printf( "%d
    ", Query( x, y ) );
    	}
    	return 0;
    }
    
  • 相关阅读:
    阿里中间件——消息中间件Notify和MetaQ
    大型网站架构系列:分布式消息队列
    Mycat
    spring集成mybatis实现mysql读写分离
    mysql5.7.18的安装与主从复制
    NuGet学习笔记(3) 搭建属于自己的NuGet服务器
    NuGet学习笔记(1)——初识NuGet及快速安装使用
    Android 中使用 html 作布局文件
    获取WebView加载HTML时网页中的内容
    android学习——popupWindow 在指定位置上的显示
  • 原文地址:https://www.cnblogs.com/chy-2003/p/11815124.html
Copyright © 2020-2023  润新知