BZOJ传送门
洛谷传送门
解析:
本来这道题ldw说是什么动态点分治?
然后被我成功用动态维护LCA水过去了。
思路:
一看直径,就是最远距离,但是这个显然用 O ( n ) O(n) O(n)的DP每次来一下是不行的。
那么重点就在距离上面,树上距离可以用什么东西维护?LCA+深度就可以 O ( log n ) O(\log n) O(logn)算出两点距离。
所以我们只需要维护每棵树的当前直径的左右端点,新加入一个点的时候处理出它的倍增数组,然后更新当前连通块的直径左右端点就行了,新的直径的两个端点必然有至少一个是原来直径的端点。这个证明十分显然,就不讨论了。
对于询问,直接查连通块里面到两个直径的距离就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
namespace IO{
inline int getint(){
re int num;
re char c;
re bool f=0;
while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
inline void outint(int a){
static char ch[23];
if(a==0)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
inline char getalpha(){
re char c;
while(!isalpha(c=gc()));
return c;
}
}
using namespace IO;
cs int N=100005,logN=17;
int block[N],l[N],r[N],bcnt;
int fa[N][logN+1],dep[N]={-1};
inline int dist(int u,int v){
re int ans=0;
if(dep[u]>dep[v])swap(u,v);
if(dep[u]<dep[v])
for(int re i=logN;~i;--i){
if(dep[fa[v][i]]>=dep[u]){
v=fa[v][i];
ans+=1<<i;
}
}
if(v==u)return ans;
for(int re i=logN;~i;--i){
if(fa[v][i]^fa[u][i]){
ans+=2<<i;
u=fa[u][i];
v=fa[v][i];
}
}
return ans+2;
}
int Q,tot;
signed main(){
Q=getint();
while(Q--){
re char op=getalpha();
re int u=getint();
switch(op){
case 'Q':{
int b=block[u];
int d1=dist(u,l[b]),d2=dist(u,r[b]);
outint(max(d1,d2));
pc('\n');
break;
}
case 'B':{
++tot;
if(~u){
block[tot]=block[u];
dep[tot]=dep[u]+1;
fa[tot][0]=u;
for(int re i=1;i<=logN;++i)
fa[tot][i]=fa[fa[tot][i-1]][i-1];
int b=block[u],d1=dist(tot,l[b]),d2=dist(l[b],r[b]);
if(d1>d2)r[b]=tot;
d1=dist(tot,r[b]);
if(d1>d2)l[b]=tot;
}
else{
block[tot]=++bcnt;
r[bcnt]=l[bcnt]=tot;
}
break;
}
}
}
return 0;
}