日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

树上倍增求LCA详解

發布時間:2023/12/10 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 树上倍增求LCA详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

LCA(least common ancestors)最近公共祖先

指的就是對于一棵有根樹,若結點z既是x的祖先,也是y的祖先(不要告訴我你不知道什么是祖先),那么z就是結點x和y的最近公共祖先。

定義到此。

那么怎么求LCA?

對于樸素思想,就是我要一步一步往上爬。。一步一步走。先把結點x和y整到同一深度,然后再一次一個深度的往上查,直到祖先一樣才break(明顯是個while)

但是,一步一步實在是太慢了,所以不能腳踏實地地走

那么,考慮跳著走,

跳著走的條件就是要滿足一步步數盡可能多并且不跳過了。當然你跳過了在一步一步往下找也行啊QWQ

于是,在滿足這兩個條件的情況下,我們有了LCA算法:

一步條2的n次方。

對于每一步跳躍,我們要預處理一個二維數組f(father)f[x][i],表示對于當前的x結點,往上跳2^i個祖先,特別的,像數學中的,f[x][0]就是x的一代祖先。于是我們要用一個dfs預處理來解決這個f數組。后面我們會提到;

處理完f數組,那就很簡單了。

輸入x和y,表示要求結點x和結點y的LCA,那么我們開始求LCA:

對于x和y在不同高度,我們先把他們拉到一個高度,同樣不能一步一步走,也要用到f數組。

這里我們要提前了解到一個定理:對于任意一個非零整數,我們都可以將他用2的次冪表示出來。也就是such as? : 11=2^3+2^1+2^0。這倒也不用證明,就像每個數都可以用二進制表示一樣。

接著講:在把x和y弄到同一高度時,我們要先做的是設x的深度dep要比y的深度dep要大,如果dep[y]>dep[x],那么把他們交換。原因是如果不交換,那么我們需要判斷兩種情況,用兩個if以及幾乎相同的代碼。。(亂七八糟還占內存

然后用一個判斷??? if(dep[f[x][i]]>=dep[y])x=f[x][i];(此時x深度大于y),在這里用外層for循環來枚舉i,但是i一定要從大到小!為什么?

安利一個故事(其實也不算):一個玻璃瓶,裝了幾塊石頭,滿到瓶口。滿了嗎?沒有。又裝了一些沙子,滿到瓶口。滿了嗎?沒有。最后又裝滿了水,滿到瓶口。終于滿了。

那么,類比于x和y的深度的距離,這個瓶子的容積也是同樣道理。從2的盡可能大的次冪去找,一旦能找到能接近他們的i,就更新dep[x],直到相等。類似于無限逼近,最后值相等的過程。如果i相等了,就說明他們的深度已經在同一層了。

與倍增到同一深度的過程相比,倍增找公共祖先也是類似的,但是有一點不同,就是你不知道什么時候找到他們的公共祖先,因此就沒有查找的上限,那么就讓他們盡可能接近。因此條件改成了這個:

if(f[x][i]!=f[y][i])
?? ??? ?{
?? ??? ??? ?x=f[x][i];y=f[y][i];
?? ??? ?}

在執行完這個語句后,我們成功讓x和y變成了某一節點z的左右兒子!

因為左右兒子是最接近但是又不相等的。

那么我們隨便取其中一個找爸爸,就找到了LCA。

啊。。。。。。喘口氣

AC代碼:

有關鏈式存圖不懂的的點這里。

?

#include<cstdio> #include<iostream> using namespace std; int n,m,s,num=0,head[1000001],dep[1000001],f[1000001][23]; int a1,a2; struct edg{int next,to; }edge[1000001]; void edge_add(int u,int v)//鏈式前向星存圖 {num++;edge[num].next=head[u];edge[num].to=v;head[u]=num;edge[++num].next=head[v];edge[num].to=u;head[v]=num; } void dfs(int u,int father)//對應深搜預處理f數組 {dep[u]=dep[father]+1;for(int i=1;(1<<i)<=dep[u];i++){f[u][i]=f[f[u][i-1]][i-1];}for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(v==father)continue;//雙向圖需要判斷是不是父親節點 f[v][0]=u;dfs(v,u);} } int lca(int x,int y) {if(dep[x]<dep[y])swap(x,y);for(int i=20;i>=0;i--)//從大到小枚舉使x和y到了同一層 {if(dep[f[x][i]]>=dep[y])x=f[x][i];if(x==y)return x;}for(int i=20;i>=0;i--)//從大到小枚舉 {if(f[x][i]!=f[y][i])//盡可能接近 {x=f[x][i];y=f[y][i];} } return f[x][0];//隨便找一個**輸出 } int main(){scanf("%d%d%d",&n,&m,&s);for(int i=1;i<n;i++){scanf("%d",&a1);scanf("%d",&a2);edge_add(a1,a2);//鏈式存邊 }dfs(s,0);for(int i=1;i<=m;i++){scanf("%d %d",&a1,&a2);printf("%d\n",lca(a1,a2));//求兩個節點的LCA } }

?

完結。。

?

轉載于:https://www.cnblogs.com/lbssxz/p/11114819.html

總結

以上是生活随笔為你收集整理的树上倍增求LCA详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。