zoukankan      html  css  js  c++  java
  • 「雅礼集训 2017 Day5」远行

    题目链接

    问题分析

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

    • 如果一棵树的直径两个端点为(a,b),那么树上一个点(v)开始的最长路径是(v ightarrow a)(v ightarrow b)
    • 如果有两棵树,直径分别为(a_1,b_1)(a_2,b_2),那么在这两棵树间连一条边,新树的直径只可能是(a_1 ightarrow b_1,a_1 ightarrow a_2, a_1 ightarrow b_2, a_2 ightarrow a_1, a_2 ightarrow b_1, a_2 ightarrow 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
    ", LastAns );
    			fflush( stdout );
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    教你如何在 Visual Studio 2013 上使用 Github
    如果你也会C#,那不妨了解下F#(1):F# 数据类型
    博客园主题美化
    适配器模式 实战
    mysql in 超过1000 解决方案
    shell: 循环日期+1
    sqoop export 报错:Got exception running Sqoop: org.kitesdk.data.DatasetNotFoundException: Descriptor location does not exist:
    git 回滚到执行版本,并推送到远程分支
    hive表增加字段,并指定字段位置
    微信抢红包架构设计
  • 原文地址:https://www.cnblogs.com/chy-2003/p/11270354.html
Copyright © 2011-2022 走看看