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