题目链接

问题分析

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

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

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

参考程序

#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;
}
01-26 00:54