2018.12.12【BZOJ5192】【洛谷P4271】New Barns(動態維護倍增陣列)
阿新 • • 發佈:2018-12-17
BZOJ傳送門
洛谷傳送門
解析:
本來這道題ldw說是什麼動態點分治?
然後被我成功用動態維護LCA水過去了。
思路:
一看直徑,就是最遠距離,但是這個顯然用 的DP每次來一下是不行的。
那麼重點就在距離上面,樹上距離可以用什麼東西維護?LCA+深度就可以 算出兩點距離。
所以我們只需要維護每棵樹的當前直徑的左右端點,新加入一個點的時候處理出它的倍增陣列,然後更新當前連通塊的直徑左右端點就行了,新的直徑的兩個端點必然有至少一個是原來直徑的端點。這個證明十分顯然,就不討論了。
對於詢問,直接查連通塊裡面到兩個直徑的距離就行了。
程式碼:
#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;
}