解析
倍增真香
关键性质:树上距离一个点最远的点必定是直径两端点其一。
本题限制好,要求少动态维护倍增数组暴力维护直径即可。
如果每次合并的是两棵树,而不是一棵树加一个点,可以先离线下来,照样能做。
如果每次强制在线,用LCT算距离即可(split出来后splay大小减1)。
如果还要支持删边,需要维护一个 fxf_xfx 表示 xxx 的splay子树内深度最浅的结点真实子树内深度最大的结点深度。(深度均指真实深度)
转移:
flsx→fxf_{ls_x}\to f_xflsx→fx
sizlsx+frsx→fxsiz_{ls_x}+f_{rs_x}\to f_xsizlsx+frsx→fx
flsx+heapx.top()→fxf_{ls_x}+heap_x.top()\to f_xflsx+heapx.top()→fx
最后一个是用堆维护虚子树内所有的 fsonf_{son}fson,然后用最大的堆顶转移。
为了支持 reversereversereverse 操作,还需要维护一个 gxg_xgx 表示 xxx 的splay子树内深度最深的结点往上延伸深度最大的结点深度,把转移式所有左右儿子互换即可。
代码
#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);#endifn=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
*/