倍增
這是最最最常見的寫法了,一個fa[N][logN]的陣列直接搞定
時間複雜度也不算太高
預處理 $ O(nlogn) $ 如果你想卡的話,可以卡到 $ O(nlogh) $ h為樹的深度
查詢 $ O(2*logn) $ 最壞,平均 $ O(logn) $
$ code $
int dfn[N],cnt;
int dep[N],fa[N][21],siz[N];
void dfs_first(int x){
dfn[x]=++cnt;
siz[x]=1;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x][0])continue;
fa[y][0]=x;
for(re i=1;i<=20;i++)fa[y][i]=fa[fa[y][i-1]][i-1];//cout<<fa[y][i]<<endl;
dep[y]=dep[x]+1;
dfs_first(y);
siz[x]+=siz[y];
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(re i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y)return x;
for(re i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
注意::::dep[1]要賦值為1,否則求取LCA時就全是0了
RMQ
這個真是神級演算法了
利用RMQ的原理找到LCA
為什麼呢? 因為在兩個點的之間,最近的公共祖先一定是,這兩個點的dfs序中,深度最小的那個
這樣問題就轉化成了在區間上求最小值,也就是RMQ問題
最最最恐怖的是他的時間複雜度
預處理與倍增相同 $ O(nlogn) $ 不能卡到h
但是查詢快爆了 $ O(1) $
$ code $
int dfn[N],cnt;
int dep[N],fa[N];
int st[N][21],lg2[N];
void dfs_first(int x){
dfn[x]=++cnt;
st[cnt][0]=x;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x])continue;
fa[y]=x;
dep[y]=dep[x]+1;
dfs_first(y);
}
}
void get_st(){
for(re i=2;i<=n;i++)lg2[i]=lg2[i>>1]+1;
for(re j=1;j<=20;j++){
for(re i=1;i+(1<<j)-1<=n;i++){
int r=i+(1<<j-1);
st[i][j]=dep[st[i][j-1]]<dep[st[r][j-1]]?st[i][j-1]:st[r][j-1];
}
}
}
int LCA(int x,int y){
x=dfn[x];y=dfn[y];
if(x>y)swap(x,y);
int tmp=lg2[y-x+1];
return dep[st[x][tmp]]<dep[st[y-(1<<tmp)+1][tmp]]?st[x][tmp]:st[y-(1<<tmp)+1][tmp];
}
所以這個還是比倍增稍微快一點的
樹鏈剖分
這個更快不信你往下看
就是簡單的根據樹鏈剖分的top一直往上跳
我們分析一下複雜度
預處理 $ O(2*n) $ 這個完全沒有爭議
查詢,這個查詢總起來是比倍增快一些的
因為一般的題不會給你出一顆滿二叉樹,所以我們每次向上跳,會遠遠少於logh
畢竟如果每次節點都在輕鏈上,高度就是 $ \frac{n}{2} $ ,沒有爭議,比倍增快
除非他卡你,就算卡你,你也是 $ \frac{n}{2} $,快快快快爆了,好像沒那麼快啊
但是還是挺快的,就是快,快,快,快,快
所以查詢的複雜度是 $ O(logn) $ 但是實際應用時,比這個快多了
$ code $
int dfn[N],cnt,fa[N];
int siz[N],son[N],dep[N],top[N];
void dfs1(int x){
dfn[x]=++cnt;siz[x]=1;son[x]=0;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x])continue;
fa[y]=x;dep[y]=dep[x]+1;
dfs1(y);siz[x]+=siz[y];
if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
}
}
void dfs2(int x,int f){
top[x]=f;
if(son[x])dfs2(son[x],f);
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==son[x]||y==fa[x])continue;
dfs2(y,y);
}
}
int LCA(int x,int y){
//cout<<x<<" "<<y<<" "<<top[x]<<" "<<top[y]<<endl;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
所以就完事了
所以好像昂還有一個tarjan的離線演算法
好像弱爆了,所以我基本不用他