「雅礼集训 2017 Day5」远行

题目链接

问题分析

要求树上最远距离,很显然就想到了树的直径。关于树的直径,有下面几个结论:

  • 如果一棵树的直径两个端点为\(a,b\),那么树上一个点\(v\)开始的最长路径是\(v\rightarrow a\)\(v \rightarrow b\)
  • 如果有两棵树,直径分别为\(a_1,b_1\)\(a_2,b_2\),那么在这两棵树间连一条边,新树的直径只可能是\(a_1\rightarrow b_1,a_1\rightarrow a_2, a_1\rightarrow b_2, a_2\rightarrow a_1, a_2 \rightarrow b_1, a_2 \rightarrow b_2\)\(6\)种可能。

那么这道题就是用并查集维护每棵树的直径的两端,然后用LCT维护树的形态即可。如果需要树的直径性质的证明,可以在下方留言。

参考程序

#include <bits/stdc++.h>
using namespace std;

const int Maxn = 300010;
int Father[ Maxn ], Child[ Maxn ][ 2 ], Stack[ Maxn ], Tag[ Maxn ];
int Size[ Maxn ];
int N, Q, Rec[ Maxn ][ 2 ], Fa[ Maxn ], Type;

void Collect( int Ind ) {
    Size[ Ind ] = Size[ Child[ Ind ][ 0 ] ] + Size[ Child[ Ind ][ 1 ] ] + 1;
    return;
}
void TagOn( int Ind ) {
    Tag[ Ind ] ^= 1;
    swap( Child[ Ind ][ 0 ], Child[ Ind ][ 1 ] );
    return;
}
void TagDown( int Ind ) {
    if( Tag[ Ind ] ) {
        TagOn( Child[ Ind ][ 0 ] );
        TagOn( Child[ Ind ][ 1 ] );
        Tag[ Ind ] ^= 1;
    }
    return;
}
bool IsRoot( int Ind ) {
    if( Ind == 0 ) return true;
    return !( ( Child[ Father[ Ind ] ][ 0 ] == Ind ) || ( Child[ Father[ Ind ] ][ 1 ] == Ind ) );
}
void Rotate( int C ) {
    int B = Father[ C ];
    int A = Father[ B ];
    int Tag = Child[ B ][ 1 ] == C;
    if( !IsRoot( B ) ) Child[ A ][ Child[ A ][ 1 ] == B ] = C;
    Father[ C ] = A;
    Child[ B ][ Tag ] = Child[ C ][ Tag ^ 1 ];
    Father[ Child[ C ][ Tag ^ 1 ] ] = B;
    Child[ C ][ Tag ^ 1 ] = B;
    Father[ B ] = C;
    Collect( B ); Collect( C );
    return;
}
void Splay( int Ind ) {
    if( Ind == 0 ) return;
    int Num = 0; Stack[ ++Num ] = Ind; int Temp = Ind;
    while( !IsRoot( Temp ) ) {
        Temp = Father[ Temp ];
        Stack[ ++Num ]  = Temp;
    }
    for( int i = Num; i >= 1; --i ) TagDown( Stack[ i ] );
    while( !IsRoot( Ind ) ) {
        int X = Father[ Ind ];
        int Y = Father[ X ];
        if( !IsRoot( X ) ) 
            if( ( Child[ Y ][ 0 ] == X ) ^ ( Child[ X ][ 0 ] == Ind ) ) 
                Rotate( Ind );
            else 
                Rotate( X );
        Rotate( Ind );
    }
    Collect( Ind );
    return;
}
void Access( int Ind ) {
    for( int i = 0; Ind; i = Ind, Ind = Father[ Ind ] ) {
        Splay( Ind ); Child[ Ind ][ 1 ] = i; Collect( Ind );
    }
    return;
}
void MakeRoot( int Ind ) {
    Access( Ind );
    Splay( Ind );
    TagOn( Ind );
    return;
}
int FindRoot( int Ind ) {
    Access( Ind ); Splay( Ind );TagDown( Ind );
    while( Child[ Ind ][ 0 ] ) {
        Ind = Child[ Ind ][ 0 ]; TagDown( Ind );
    }
    Splay( Ind );
    return Ind;
}
void Split( int x, int y ) { 
    MakeRoot( x );
    Access( y );
    Splay( y );
    return; 
}
void Link( int x, int y ) {
    MakeRoot( x );
    if( FindRoot( y ) == x ) return;
    Father[ x ] = y;
    return;
}
void Cut( int x, int y ) {
    MakeRoot( x );
    if( FindRoot( y ) != x || Child[ y ][ 0 ] || Father[ y ] != x ) return;
    Father[ y ] = Child[ x ][ 1 ] = 0;
    Collect( x );
    return;
}

int GetFather( int x ) {
    if( Fa[ x ] == x ) return x;
    Fa[ x ] = GetFather( Fa[ x ] );
    return Fa[ x ];
}

int main() {
    scanf( "%d", &Type );
    scanf( "%d%d", &N, &Q );
    for( int i = 1; i <= N; ++i ) {
        Fa[ i ] = i;
        Size[ i ] = 1;
        Rec[ i ][ 0 ] = Rec[ i ][ 1 ] = i;
    }
    int LastAns = 0;
    for( int i = 1; i <= Q; ++i ) {
        int Opt; scanf( "%d", &Opt );
        if( Opt == 1 ) {
            int u, v; scanf( "%d%d", &u, &v );
            if( Type ) u ^= LastAns, v ^= LastAns;
            int U = GetFather( u ), V = GetFather( v );
            if( U == V ) continue;

            int Max = 0, x, y;
            Split( Rec[ U ][ 0 ], Rec[ U ][ 1 ] );
            if( Size[ Rec[ U ][ 1 ] ] > Max ) {
                Max = Size[ Rec[ U ][ 1 ] ];
                x = Rec[ U ][ 0 ]; y = Rec[ U ][ 1 ];
            }
            Split( Rec[ V ][ 0 ], Rec[ V ][ 1 ] );
            if( Size[ Rec[ V ][ 1 ] ] > Max ) {
                Max = Size[ Rec[ V ][ 1 ] ];
                x = Rec[ V ][ 0 ]; y = Rec[ V ][ 1 ];
            }
            Link( u, v ); Fa[ U ] = V;
            for( int j = 0; j < 2; ++j )
                for( int k = 0; k < 2; ++k ) {
                    Split( Rec[ U ][ j ], Rec[ V ][ k ] );
                    if( Size[ Rec[ V ][ k ] ] > Max ) {
                        Max = Size[ Rec[ V ][ k ] ];
                        x = Rec[ U ][ j ]; y = Rec[ V ][ k ];
                    }
                }
            Rec[ V ][ 0 ] = x; Rec[ V ][ 1 ] = y;
        } else {
            int u; scanf( "%d", &u );
            if( Type ) u ^= LastAns;
            int U = GetFather( u );
            LastAns = 0;
            for( int j = 0; j < 2; ++j ) {
                Split( Rec[ U ][ j ], u );
                LastAns = max( LastAns, Size[ u ] );
            }
            --LastAns;
            printf( "%d\n", LastAns );
            fflush( stdout );
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/chy-2003/p/11270354.html
今日推荐