洛谷P4271:New Barns P(倍增)(LCT)(直径)

这篇博客介绍了如何利用树状数组和最近公共祖先(LCA)数据结构来解决树上的直径问题。文章详细阐述了关键性质,并提供了动态维护直径的解决方案,包括离线处理、在线处理以及支持撤销操作的情况。代码示例展示了如何使用这些数据结构进行高效计算。
摘要由CSDN通过智能技术生成

解析

倍增真香

关键性质:树上距离一个点最远的点必定是直径两端点其一。
本题限制好,要求少动态维护倍增数组暴力维护直径即可。
如果每次合并的是两棵树,而不是一棵树加一个点,可以先离线下来,照样能做。
如果每次强制在线,用LCT算距离即可(split出来后splay大小减1)。
如果还要支持删边,需要维护一个 f x f_x fx 表示 x x xsplay子树内深度最浅的结点真实子树内深度最大的结点深度。(深度均指真实深度)
转移:
f l s x → f x f_{ls_x}\to f_x flsxfx
s i z l s x + f r s x → f x siz_{ls_x}+f_{rs_x}\to f_x sizlsx+frsxfx
f l s x + h e a p x . t o p ( ) → f x f_{ls_x}+heap_x.top()\to f_x flsx+heapx.top()fx
最后一个是用堆维护虚子树内所有的 f s o n f_{son} fson,然后用最大的堆顶转移。
为了支持 r e v e r s e reverse reverse 操作,还需要维护一个 g x g_x gx 表示 x x xsplay子树内深度最深的结点往上延伸深度最大的结点深度,把转移式所有左右儿子互换即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
const int N=1e5+100;
const int M=2e4+100;
const int inf=1e9;

int n,tot;

int pl[N][20],dep[N];
int fa[N],u[N],v[N],d[N];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
char c;
int x;
inline int Lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int k=17;k>=0;k--){
		if(dep[pl[x][k]]>=dep[y]) x=pl[x][k];
	}
	if(x==y) return x;
	for(int k=17;k>=0;k--){
		if(pl[x][k]==pl[y][k]) continue;
		x=pl[x][k];y=pl[y][k];
	}
	return pl[x][0];
}
inline int dis(int x,int y){
	int lca=Lca(x,y);
	return dep[x]+dep[y]-2*dep[lca];
}
signed main(){
	#ifndef ONLINE_JUDGE
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	#endif
	n=read();
	while(n--){
		scanf(" %c%d",&c,&x);
		if(c=='B'){
			int now=++tot;
			if(x==-1) x=0;
			pl[now][0]=x;
			for(int k=1;pl[now][k-1];++k) pl[now][k]=pl[pl[now][k-1]][k-1];
			dep[now]=dep[x]+1;
			if(x){
				int o=find(x),tmp(0);
				fa[now]=o;
				if((tmp=dis(now,u[o]))>d[o]){
					v[o]=now;d[o]=tmp;
				}
				if((tmp=dis(now,v[o]))>d[o]){
					u[o]=now;d[o]=tmp;
				}
			}
			else{
				fa[now]=now;u[now]=v[now]=now;d[now]=0;
			}
		}
		else{
			int o=find(x);
			printf("%d\n",max(dis(u[o],x),dis(v[o],x)));
		}
	}
	return 0;
}
/*
10 10
1 8 5
1 6 5
1 7 9
1 4 2
1 3 4
1 4 2
1 6 3
1 4 1
1 7 1
1 8 4
*/
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值