「雅礼集训 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;
}
原文地址:https://www.cnblogs.com/chy-2003/p/11270354.html