「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\n", 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( "\n" );
#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\n", 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\n", 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\n", u, v );
		printf( "    MidDeep = %d, Dis1 = %d, Dis2 = %d\n", MidDeep, Dis1, Dis2 );
#endif
		Ans[ Belong[ u ] ] -= MainTree::Size[ SpotTop ];
#ifdef Debug 
		printf( "        %d -= %d, %d = %d\n", 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\n", 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\n", 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\n", 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( "\n" );
		return;
	}
} //VirtualTree

void Work( int Flag ) {
	VirtualTree::Init( Flag );
	VirtualTree::GetMinDis();
	VirtualTree::Cal();
	VirtualTree::Print();
	return;
}

int main() {
	MainTree::Init();
#ifdef Debug 
	printf( "***\n" );
#endif
	int TestCases;
	scanf( "%d", &TestCases );
	for( ; TestCases; --TestCases ) Work( TestCases );
	return 0;
}
posted @ 2019-09-30 11:40  chy_2003  阅读(110)  评论(0编辑  收藏  举报