「雅礼集训 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;
}
「雅礼集训 2017 Day5」远行的更多相关文章
- LOJ#6038. 「雅礼集训 2017 Day5」远行(LCT)
题面 传送门 题解 要不是因为数组版的\(LCT\)跑得实在太慢我至于去学指针版的么--而且指针版的完全看不懂啊-- 首先有两个结论 1.与一个点距离最大的点为任意一条直径的两个端点之一 2.两棵树之 ...
- 【loj6038】「雅礼集训 2017 Day5」远行 树的直径+并查集+LCT
题目描述 给你 $n$ 个点,支持 $m$ 次操作,每次为以下两种:连一条边,保证连完后是一棵树/森林:询问一个点能到达的最远的点与该点的距离.强制在线. $n\le 3\times 10^5$ ,$ ...
- 【刷题】LOJ 6038 「雅礼集训 2017 Day5」远行
题目描述 Miranda 生活的城市有 \(N\) 个小镇,一开始小镇间没有任何道路连接.随着经济发现,小镇之间陆续建起了一些双向的道路但是由于经济不太发达,在建设过程中,会保证对于任意两个小镇,最多 ...
- [loj6038]「雅礼集训 2017 Day5」远行 lct+并查集
给你 n 个点,支持 m 次操作,每次为以下两种:连一条边,保证连完后是一棵树/森林:询问一个点能到达的最远的点与该点的距离.强制在线. n≤3×10^5 n≤3×10^5 ,m≤5×10^5 m≤5 ...
- loj#6038 「雅礼集训 2017 Day5」远行
分析 代码 #include<bits/stdc++.h> using namespace std; #define fi first #define se second #define ...
- loj6038「雅礼集训 2017 Day5」远行 树的直径+并查集+LCT
题目传送门 https://loj.ac/problem/6038 题解 根据树的直径的两个性质: 距离树上一个点最远的点一定是任意一条直径的一个端点. 两个联通块的并的直径是各自的联通块的两条直径的 ...
- LOJ#6038. 「雅礼集训 2017 Day5」远行 [LCT维护子树的直径]
树的直径一定是原联通块4个里的组合 1.LCT,维护树的直径,这题就做完了 2.直接倍增,lca啥的求求距离,也可以吧- // powered by c++11 // by Isaunoya #inc ...
- 「雅礼集训 2017 Day5」珠宝
题目描述 Miranda 准备去市里最有名的珠宝展览会,展览会有可以购买珠宝,但可惜的是只能现金支付,Miranda 十分纠结究竟要带多少的现金,假如现金带多了,就会比较危险,假如带少了,看到想买的右 ...
- 「雅礼集训 2017 Day5」矩阵
填坑填坑.. 感谢wwt耐心讲解啊.. 如果要看这篇题解建议从上往下读不要跳哦.. 30pts 把$A$和$C$看成$n$个$n$维向量,那$A_i$是否加入到$C_j$中就可以用$B_{i,j}$表 ...
随机推荐
- js执行多次事件,而非一次
晚上查阅了很多文章,都是避免点击事件多次执行.反过来要是让事件多次执行该如何做? 这里可以配个setTimeout():来执行 这里我们用layui <link rel="styles ...
- SpringBoot-2-基本配置
自定义启动配置 在resources下面新建一个banner.txt文件,里面写入自己想要的内容 /////////////////////////////////////////////////// ...
- linux安装git服务器和svn服务器
linux版本 linux版本为CentOS 6.8 (要注意有些软件的安装方法在各个linux版本之间也是存在差异的) git服务器 git服务器需要提供一个UI供开发人员创建项目管理项目,选择使用 ...
- Codeforces 1196D2. RGB Substring (hard version)
传送门 考虑枚举每一个位置作为可能子段的起点,然后对以这个位置为起点的所有情况下的答案取 $min$ 当固定了起点 $i$ 并且固定了起点 $i$ 最终的字符时,答案也固定了 发现对于所有与 $i \ ...
- Codeforces 1221E. Game With String
传送门 首先每一段连续的 $...$ 都是互不影响的,所以可以一段段考虑 考虑最简单的情况,此时每一段都大于等于 $a$ 并且小于 $2b$ ,那么每一段都只能放一次,胜负直接根据段数即可得到答案 考 ...
- Constructing Tests CodeForces - 938C
大意: 定义m-free矩阵: 所有$m*m$的子矩阵至少有一个$0$的$01$矩阵. 定义一个函数$f(n,m)=n*n$的m-free矩阵最大$1$的个数. 给出$t$个询问, 每个询问给出$x$ ...
- leecode100热题 HOT 100
# 题名 题解 通过率 难度 出现频率 1 两数之和 46.5% 简单 2 两数相加 35.5% 中等 3 无重复字符的最长子串 31.1% ...
- response.getWriter().wirte和out.print()的区别
1.首先介绍write()和print()方法的区别: (1).write():仅支持输出字符类型数据,字符.字符数组.字符串等 (2).print():可以将各种类型(包括Object)的数据通 ...
- JavaScript的数组方法(array)
数组方法: 1. concat() 合并数组 2. join() 将数组的元素拼接成字符串,并指定分隔符 3. push() 往数组末尾添加一个元素,并返回新的数组的长度 4. reverse( ...
- Qt 按键键值 与 相关字符串 的映射表(转)
Qt快捷键 映射 "Esc",/*Qt::Key_Escape 0x01000000 */ "Tab",/*Qt::Key_Tab 0x01000001 ...