【Loj】 6038「雅礼集训 2017 Day5」远行

这篇文章介绍了如何利用LCT(Link-CutTree)数据结构来解决在动态树上维护直径的问题。在合并两棵树时,文章讨论了可能出现的6种情况,并展示了如何更新树的直径。同时,代码实现中包含了树的旋转、访问、使节点成为根节点等关键操作,以及如何处理加边操作和查询节点间距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

点击打开链接

题目解法

可以发现 x x x 到的最远距离的点一定是 x x x 所在树的直径的两个端点之一
这个可以用反证法证明

于是考虑维护每个树的直径的两个端点
考虑合并 2 2 2 个树,如果第一个树直径的两个端点分别是 l 1 , r 1 l1,r1 l1,r1,第二个树直径的两个端点是 l 2 , r 2 l2,r2 l2,r2,这两棵树是通过 x − y x-y xy 连接起来的,那么新树的直径有 6 6 6 种情况,既考虑在原来 2 2 2 棵树内的 2 2 2 种情况,和跨树的 6 6 6 种情况

现在需要快速求出树中 2 2 2 个点的距离,且支持加边操作
可以自然想到用 l c t lct lct 维护一下

时间复杂度 O ( q l o g n ) O(qlogn) O(qlogn),常数比较大

#include <bits/stdc++.h>
using namespace std;
const int N(300100);
struct Node{
	int s[2],fa,tagrev,siz;
}tr[N];
int n,q,stk[N]; 
int l[N],r[N],fa[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
bool isroot(int x){
	return tr[tr[x].fa].s[1]!=x&&tr[tr[x].fa].s[0]!=x;
}
void pushrev(int x){
	swap(tr[x].s[0],tr[x].s[1]);
	tr[x].tagrev^=1;
}
void pushdown(int x){
	if(tr[x].tagrev) pushrev(tr[x].s[0]),pushrev(tr[x].s[1]);
	tr[x].tagrev=0;
}
void pushup(int x){
	tr[x].siz=tr[tr[x].s[0]].siz+tr[tr[x].s[1]].siz+1;
}
void rotate(int x){
	int y=tr[x].fa,z=tr[y].fa;
	if(!isroot(y)) tr[z].s[tr[z].s[1]==y]=x;
	tr[x].fa=z;
	int k=tr[y].s[1]==x;
	tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].fa=y;
	tr[x].s[k^1]=y,tr[y].fa=x;
	pushup(y),pushup(x);
}
void splay(int x){
	int top=0,t=x;stk[++top]=t;
	while(!isroot(t)) t=tr[t].fa,stk[++top]=t;
	while(top) pushdown(stk[top--]);
	while(!isroot(x)){
		int y=tr[x].fa,z=tr[y].fa;
		if(!isroot(y))
			if((tr[z].s[1]==y)^(tr[y].s[1]==x)) rotate(x);
			else rotate(y);
		rotate(x);
	}
}
void access(int x){
	int z=x;
	for(int y=0;x;y=x,x=tr[x].fa){
		splay(x);
		tr[x].s[1]=y,pushup(x);
	}
	splay(z);
}
void make_root(int x){
	access(x),pushrev(x);
}
int find_root(int x){
	access(x);
	while(tr[x].s[0]) x=tr[x].s[0];
	splay(x);
	return x;
}
void link(int x,int y){
	make_root(x);
	if(find_root(y)!=x) tr[x].fa=y;
}
int get_father(int x){
	return x==fa[x]?x:fa[x]=get_father(fa[x]);
}
void split(int x,int y){
	make_root(x),access(y);
}
int get_dis(int x,int y){
	split(x,y),splay(x);
	return tr[x].siz;
}
int main(){
	int type=read();
	n=read(),q=read();
	for(int i=1;i<=n;i++) l[i]=r[i]=i,fa[i]=i;
	int lastans=0;
	for(int i=1,op,x,y;i<=q;i++){
		op=read(),x=read();
		if(op==1) y=read();
		if(type) x^=lastans,y^=lastans;
		if(op==1){
			int fax=get_father(x),fay=get_father(y);
			link(x,y);
			fa[fax]=fay;
			int l1=l[fax],r1=r[fax],l2=l[fay],r2=r[fay];
			if(get_dis(l1,r1)>get_dis(l[fay],r[fay]))
				l[fay]=l1,r[fay]=r1;
			if(get_dis(l1,x)+get_dis(y,l2)+1>get_dis(l[fay],r[fay]))
				l[fay]=l1,r[fay]=l2;
			if(get_dis(l1,x)+get_dis(y,r2)+1>get_dis(l[fay],r[fay]))
				l[fay]=l1,r[fay]=r2;
			if(get_dis(r1,x)+get_dis(y,l2)+1>get_dis(l[fay],r[fay]))
				l[fay]=r1,r[fay]=l2;
			if(get_dis(r1,x)+get_dis(y,r2)+1>get_dis(l[fay],r[fay]))
				l[fay]=r1,r[fay]=r2;
		}
		else{
			int fax=get_father(x);
			printf("%d\n",lastans=max(get_dis(x,l[fax]),get_dis(x,r[fax]))-1);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值