• 「HNOI2014」世界树


    题目链接

    问题分析

    首先观察数据范围可以知道要用虚树。但是要考虑怎么维护原树的距离信息。

    如果只有两个关键点,我们可以很方便地找到中点将整棵树划分为两部分。而如果有多个关键点,看起来有效的方法就是多源的BFS。而虚树上边的长度不相同,分割点又不一定在虚树上,所以这个方法并不那么适用。

    考虑到虚树实际上维护了所有关键点和LCA之间的关系。这样一来,虚树上相邻的两个点一定是原树上祖先与后代的关系。而且这条链上所挂的其他子树里面没有关键点。这样一来,对于这一条链,如果我们知道了两个端点分别属于哪个关键点,那么我们可以简单的通过原树上子树的size加减来求得答案。所以我们得到了这样的做法:

    在虚树上跑两遍DP,求出虚树上每个点最近的关键点位置及其距离。

    为了较为良好地处理边界问题,我们这样统计答案:

    对于一个点,我们首先假设 原树中以这个点为根的子树上的点 都属于 这个点属于的关键点。然后遍历它的儿子。对于每个儿子,找到分界点,然后减去不属于当前点的位置(通过在原树上的深度计算,然后倍增确定位置)。注意这里的链并不包括当前点和它的儿子。这样遍历完整棵虚树就可以得到答案了。

    参考程序

    loj不加读入优化,不加奇怪模板中最长代码

    #include <bits/stdc++.h>
    //#define Debug
    using namespace std;
    
    const int Maxn = 300010;
    const int MaxLog = 20;
    const int INF = 100000010;
    struct edge {
    	int To, Next;
    	edge() {}
    	edge( int _To, int _Next ) : To( _To ), Next( _Next ) {}
    };
    struct tree {
    	int Start[ Maxn ], Used, Flag[ Maxn ], State;
    	edge Edge[ Maxn << 1 ];
    	tree() {
    		State = Used = 0;
    		memset( Flag, 255, sizeof( Flag ) );
    		return;
    	}
    	inline void Set( int _State ) {
    		Used = 0; State = _State;
    		return;
    	}
    	inline void AddDirectedEdge( int x, int y ) {
    		if( Flag[ x ] != State ) {
    			Flag[ x ] = State;
    			Start[ x ] = 0;
    		}
    		Edge[ ++Used ] = edge( y, Start[ x ] );
    		Start[ x ] = Used;
    		return;
    	}
    	inline void AddUndirectedEdge( int x, int y ) {
    #ifdef Debug 
    		printf( "AddEdge %d %d
    ", x, y );
    #endif
    		AddDirectedEdge( x, y );
    		AddDirectedEdge( y, x );
    	}
    };
    
    namespace MainTree {
    	tree Tree;
    	int n, Deep[ Maxn ], D[ Maxn ][ MaxLog ], Dfn[ Maxn ], Time, Size[ Maxn ];
    	void Build( int u, int Fa ) {
    		Size[ u ] = 1;
    		D[ u ][ 0 ] = Fa;
    		Dfn[ u ] = ++Time;
    		Deep[ u ] = Deep[ Fa ] + 1;
    		for( int i = 1; i < MaxLog; ++i ) 
    			D[ u ][ i ] = D[ D[ u ][ i - 1 ] ][ i - 1 ];
    		for( int t = Tree.Start[ u ]; t; t = Tree.Edge[ t ].Next ) {
    			int v = Tree.Edge[ t ].To;
    			if( v == Fa ) continue;
    			Build( v, u );
    			Size[ u ] += Size[ v ];
    		}
    		return;
    	}
    	void Init() {
    		Tree.Set( 0 );
    		scanf( "%d", &n );
    		for( int i = 1; i < n; ++i ) {
    			int x, y;
    			scanf( "%d%d", &x, &y );
    			Tree.AddUndirectedEdge( x, y );
    		}
    		Build( 1, 0 );
    		return;
    	}
    	int GetLca( int x, int y ) {
    		if( Deep[ x ] < Deep[ y ] ) swap( x, y );
    		for( int i = MaxLog - 1; i >= 0; --i ) 
    			if( Deep[ D[ x ][ i ] ] >= Deep[ y ] )
    				x = D[ x ][ i ];
    		if( x == y ) return x;
    		for( int i = MaxLog - 1; i >= 0; --i )
    			if( D[ x ][ i ] != D[ y ][ i ] ) {
    				x = D[ x ][ i ];
    				y = D[ y ][ i ];
    			}
    		return D[ x ][ 0 ];
    	}
    } //MainTree
    
    namespace VirtualTree {
    	bool Cmp( int x, int y ) {
    		return MainTree::Dfn[ x ] < MainTree::Dfn[ y ];
    	}
    	int m, h[ Maxn ], Important[ Maxn ], Stack[ Maxn ], Flag;
    	int Rec[ Maxn ];
    	tree Tree; 
    	int Ans[ Maxn ], Deg[ Maxn ];
    	void Init( int _Flag ) { 
    		Flag = _Flag;
    		Tree.Set( Flag );
    		scanf( "%d", &m );
    		for( int i = 1; i <= m; ++i ) scanf( "%d", &h[ i ] );
    		for( int i = 1; i <= m; ++i ) Rec[ i ] = h[ i ];
    		sort( h + 1, h + m + 1, Cmp );
    #ifdef Debug 
    		for( int i = 1; i <= m; ++i ) printf( "%d ", Rec[ i ] ); printf( "
    " );
    #endif
    		for( int i = 1; i <= m; ++i ) Important[ h[ i ] ] = Flag;
    		for( int i = 1; i <= m; ++i ) Deg[ i ] = 0;
    		Stack[ 0 ] = Stack[ 1 ] = 1;
    		for( int i = 1; i <= m; ++i ) {
    			if( i == 1 && h[ i ] == 1 ) continue;
    			int Lca = MainTree::GetLca( h[ i ], Stack[ Stack[ 0 ] ] );
    #ifdef Debug
    			printf( "%d %d, Lca = %d
    ", h[ i ], Stack[ Stack[ 0 ] ], Lca );
    #endif
    			if( MainTree::Deep[ Lca ] == MainTree::Deep[ Stack[ Stack[ 0 ] ] ] ) {
    				Stack[ ++Stack[ 0 ] ] = h[ i ];
    			} else {
    				while( MainTree::Deep[ Lca ] < MainTree::Deep[ Stack[ Stack[ 0 ] - 1 ] ] ) {
    					Tree.AddUndirectedEdge( Stack[ Stack[ 0 ] ], Stack[ Stack[ 0 ] - 1 ] );
    					++Deg[ Stack[ Stack[ 0 ] - 1 ] ];
    					--Stack[ 0 ];
    				}
    				if( MainTree::Deep[ Lca ] == MainTree::Deep[ Stack[ Stack[ 0 ] - 1 ] ] ) {
    					Tree.AddUndirectedEdge( Stack[ Stack[ 0 ] ], Stack[ Stack[ 0 ] - 1 ] );
    					++Deg[ Stack[ Stack[ 0 ] - 1 ] ];
    					--Stack[ 0 ];
    					Stack[ ++Stack[ 0 ] ] = h[ i ];
    				} else {
    					Tree.AddUndirectedEdge( Stack[ Stack[ 0 ] ], Lca );
    					++Deg[ Lca ];
    					--Stack[ 0 ];
    					Stack[ ++Stack[ 0 ] ] = Lca;
    					Stack[ ++Stack[ 0 ] ] = h[ i ];
    				}
    			}
    		}
    		while( Stack[ 0 ] > 1 ) {
    			Tree.AddUndirectedEdge( Stack[ Stack[ 0 ] ], Stack[ Stack[ 0 ] - 1 ] );
    			++Deg[ Stack[ Stack[ 0 ] - 1 ] ];
    			--Stack[ 0 ];
    		}
    		return;
    	}
    	int Belong[ Maxn ], Dis[ Maxn ];
    	void Dfs1( int u, int Fa ) {
    		Belong[ u ] = 0;
    		if( Important[ u ] == Flag ) {
    			Dis[ u ] = 0;
    			Belong[ u ] = u;
    		} else {
    			Dis[ u ] = INF;
    			Belong[ u ] = INF;
    		}
    		for( int t = Tree.Start[ u ]; t; t = Tree.Edge[ t ].Next ) {
    			int v = Tree.Edge[ t ].To;
    			if( v == Fa ) continue;
    			Dfs1( v, u );
    			int T = Dis[ v ] + MainTree::Deep[ v ] - MainTree::Deep[ u ];
    			if( T < Dis[ u ] || ( T == Dis[ u ] && Belong[ v ] < Belong[ u ] ) ) {
    				Dis[ u ] = T;
    				Belong[ u ] = Belong[ v ];
    			}
    		}
    		return;
    	}
    	void Dfs2( int u, int Fa, int _Dis, int _Belong ) {
    		if( _Dis < Dis[ u ] || ( _Dis == Dis[ u ] && _Belong < Belong[ u ] ) ) {
    			Dis[ u ] = _Dis;
    			Belong[ u ] = _Belong;
    		}
    		if( Dis[ u ] < _Dis || ( Dis[ u ] == _Dis && Belong[ u ] < _Belong ) ) {
    			_Dis = Dis[ u ];
    			_Belong = Belong[ u ];
    		}
    		for( int t = Tree.Start[ u ]; t; t = Tree.Edge[ t ].Next ) {
    			int v = Tree.Edge[ t ].To;
    			if( v == Fa ) continue;
    			Dfs2( v, u, _Dis + MainTree::Deep[ v ] - MainTree::Deep[ u ], _Belong );
    		}
    #ifdef Debug 
    		printf( "u = %d, Dis = %d, Belong = %d
    ", u, Dis[ u ], Belong[ u ] );
    #endif
    		return;
    	}
    	void GetMinDis() {
    		Dfs1( 1, 0 );
    		Dfs2( 1, 0, INF, INF );
    		return;
    	}
    	void Cal( int u, int v ) {
    		int MidDeep = ( MainTree::Deep[ u ] - Dis[ u ] + 1
    				+ MainTree::Deep[ v ] + Dis[ v ] - 1 ) / 2;
    		int Dis1 = MidDeep - ( MainTree::Deep[ u ] - Dis[ u ] + 1 );
    		int Dis2 = ( MainTree::Deep[ v ] + Dis[ v ] - 1 ) - MidDeep;
    		if( Dis1 == Dis2 && Belong[ v ] < Belong[ u ] ) --MidDeep;
    		Dis1 = MidDeep - ( MainTree::Deep[ u ] - Dis[ u ] + 1 );
    		Dis2 = ( MainTree::Deep[ v ] + Dis[ v ] - 1 ) - MidDeep;
    		int SpotMid = v;
    		for( int i = MaxLog - 1; i >= 0; --i )
    			if( MainTree::Deep[ MainTree::D[ SpotMid ][ i ] ] > MidDeep )
    				SpotMid = MainTree::D[ SpotMid ][ i ];
    		int SpotTop = v;
    		for( int i = MaxLog - 1; i >= 0; --i )
    			if( MainTree::Deep[ MainTree::D[ SpotTop ][ i ] ] > MainTree::Deep[ u ] )
    				SpotTop = MainTree::D[ SpotTop ][ i ];
    #ifdef Debug 
    		printf( "    Link %d %d
    ", u, v );
    		printf( "    MidDeep = %d, Dis1 = %d, Dis2 = %d
    ", MidDeep, Dis1, Dis2 );
    #endif
    		Ans[ Belong[ u ] ] -= MainTree::Size[ SpotTop ];
    #ifdef Debug 
    		printf( "        %d -= %d, %d = %d
    ", Belong[ u ], MainTree::Size[ SpotTop ], Belong[ u ], Ans[ Belong[ u ] ] );
    #endif
    		Ans[ Belong[ u ] ] += MainTree::Size[ SpotTop ] - MainTree::Size[ SpotMid ];
    #ifdef Debug 
    		printf( "        %d += %d, %d = %d
    ", Belong[ u ], MainTree::Size[ SpotTop ] - MainTree::Size[ SpotMid ], Belong[ u ], Ans[ Belong[ u ] ] );
    #endif
    		Ans[ Belong[ v ] ] += MainTree::Size[ SpotMid ] - MainTree::Size[ v ];
    #ifdef Debug 
    		printf( "        %d += %d, %d = %d
    ", Belong[ v ], MainTree::Size[ SpotMid ] - MainTree::Size[ v ], Belong[ v ], Ans[ Belong[ v ] ] );
    #endif
    		return;
    	}
    	void Dfs3( int u, int Fa ) {
    		Ans[ Belong[ u ] ] += MainTree::Size[ u ];
    #ifdef Debug 
    		printf( "Point %d, %d += %d, %d = %d
    ", u, Belong[ u ], MainTree::Size[ u ], Belong[ u ], Ans[ Belong[ u ] ] );
    #endif
    		for( int t = Tree.Start[ u ]; t; t = Tree.Edge[ t ].Next ) {
    			int v = Tree.Edge[ t ].To;
    			if( v == Fa ) continue;
    			Cal( u, v );
    			Dfs3( v, u );
    		}
    		return;
    	}
    	void Cal() {
    		for( int i = 1; i <= m; ++i ) Ans[ h[ i ] ] = 0;
    		Dfs3( 1, 0 );
    		return;
    	}
    	void Print() {
    		for( int i = 1; i <= m; ++i ) 
    			printf( "%d ", Ans[ Rec[ i ] ] );
    		printf( "
    " );
    		return;
    	}
    } //VirtualTree
    
    void Work( int Flag ) {
    	VirtualTree::Init( Flag );
    	VirtualTree::GetMinDis();
    	VirtualTree::Cal();
    	VirtualTree::Print();
    	return;
    }
    
    int main() {
    	MainTree::Init();
    #ifdef Debug 
    	printf( "***
    " );
    #endif
    	int TestCases;
    	scanf( "%d", &TestCases );
    	for( ; TestCases; --TestCases ) Work( TestCases );
    	return 0;
    }
    
  • 相关阅读:
    【数据结构】线性表&&顺序表详解和代码实例
    【智能算法】超详细的遗传算法(Genetic Algorithm)解析和TSP求解代码详解
    【智能算法】用模拟退火(SA, Simulated Annealing)算法解决旅行商问题 (TSP, Traveling Salesman Problem)
    【智能算法】迭代局部搜索(Iterated Local Search, ILS)详解
    10. js时间格式转换
    2. 解决svn working copy locked问题
    1. easyui tree 初始化的两种方式
    10. js截取最后一个斜杠后面的字符串
    2. apache整合tomcat部署集群
    1. apache如何启动
  • 原文地址:https://www.cnblogs.com/chy-2003/p/11611892.html
Copyright © 2020-2023  润新知