求LCA——最近公共祖先 倍增演算法
LCA是啥呢,LCA就是一棵樹裡兩個節點的最近公共祖先,如下圖
2號節點和3號節點的LCA就是1, 5號節點和11號節點的LCA就是2,8號節點和4號節點的lca就是4
那麼怎麼求LCA呢。首先要建樹,然後最容易想到的就是兩個節點一起向上跳,第一個相遇的節點就是LCA
輸入輸出格式可參考洛谷P3379 LCA模板題
輸入格式:
第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的序號。
接下來N-1行每行包含兩個正整數x、y,表示x結點和y結點之間有一條直接連線的邊(資料保證可以構成樹)。
接下來M行每行包含兩個正整數a、b,表示詢問a結點和b結點的最近公共祖先。
輸出格式:
輸出包含M行,每行包含一個正整數,依次為每一個詢問的結果。
先用鄰接表存路徑。
void add(int x,int y) { to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; } int main() { n=read();m=read();s=read(); int i,x,y; for(i=1;i<n;i++) { x=read();y=read(); add(x,y); add(y,x); } return 0; }
存路徑後,我們用dfs建樹,把每個節點的深度記錄下來
void dfs(int x,int fa)//fa表示父節點編號
{
int i;
f[x]=fa;//f[x]表示x節點的父節點
d[x]=d[fa]+1;
for(i=head[x];i;i=nxt[i])
if(to[i]!=fa)
dfs(to[i],x);
}
其次需要先把兩個節點跳到同一深度,然後一起往上跳即可
int work(int x,int y) { int i; if(d[x]<d[y]) swap(x,y); while(d[x]>d[y]) x=f[x];//移動至同一深度 while(x!=y) x=f[x],y=f[y]; return x; }
以上就是暴力求LCA,以為能AC嗎??妥妥的TLE!!!
那麼為什麼T呢?是因為一次只移動到父親節點要跳好多次,這樣太慢了!
那麼怎麼才能快點呢?就要用倍增了!設一個grd[x][i]陣列表示x節點想上跳2的i次方後的節點。所以i<=log2(n)
這個陣列怎麼用呢?令d[x]>=d[y]。首先還是要跳到同一深度中,先讓x跳2^20次(最大)。假設我們發現深度比y小了,這就表示跳過了,我們再試試跳2^19次,如果還過了,那就再變小。如果發現深度還沒到,那就跳唄,接著我們再跳,就該試試跳2^18次了,為什麼不能跳兩個2^19次呢?這是以為兩個2^19次就是2^20次啊,肯定可以不用跳兩次相同的。這樣跳就能大大減少時間了。
到同一深度後,就是兩個點一起往上跳了。我們還是讓兩個點跳2^20次,如果發現跳完後相同了,就可以確定跳到祖先了,但不一定是最近的,所以我們先不著急跳,接著,試試跳2^19次,如果還是相同,那就再變小,如果不相同,那就跳(跟上面差不多),最後跳完,可以確定x和y再向上跳一次就是LCA了,因為我們跳的時候判定了如果跳之後相同那就不跳。但是注意,當y是x的祖先時,跳到同一深度時x==y已經是LCA了,此時就不用再往上跳了。
舉個栗子,還是剛才那張圖,我們模擬一下求11和7的LCA
首先移動到同一深度,d[7]=3,d[11]=4,所以我們從2^20次(最大)開始,一直到2^1都不行,所以節點11跳了2^0次跳到了節點6。
接著兩個節點一塊兒跳,從2^20次,最後還是隻能跳2^0次,變成了節點2和節點3,最終再跳一次即節點1就是LCA。
em……這個栗子有點小。。。。。。。
好了,那怎麼預處理grd陣列呢?我們可以在dfs建樹的時候預處理,那麼我們可以確定grd[x][0]就是他的爸爸,那麼怎麼確定grd[x][1]呢?grd[x][1]不就是爸爸的爸爸(爺爺)嘛,所以grd[x][1]=grd [ grd [ x ] [ 0 ] ] [ 0 ](為了讓大家看清楚特意用空格隔開嘿嘿嘿),那麼grd[x][2]呢(注意這是爺爺的爺爺,而不是爺爺的爸爸)?爺爺的爺爺不就等於grd [ grd [ x ] [ 1 ] ] [ 1 ]嘛。
以此類推,我們得出grd[x][i]=grd[ grd [ x ] [ i - 1 ] ] [ i - 1 ]。是因為2^(i-1)+2(i-1)=2^i,即往上跳兩次2^(i-1)就是往上跳2^i
好了我們再捋一遍思路,鄰接表+預處理grd陣列+跳到相同深度+一起往上跳,程式碼如下。
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
inline int read()
{
int f=1,x=0;
char c=getchar();
while(!isdigit(c)){if(c=='-')f*=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int N=5e5+5;
int a[N],n,m,s,to[N<<1],head[N],nxt[N<<1],grd[N][23],d[N],cnt;
void add(int x,int y)
{
to[++cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;//鄰接表
}
void dfs(int x,int fa)
{
int i;
grd[x][0]=fa;//父節點
d[x]=d[fa]+1;
for(i=1;i<21;i++)//其實到本題19就可以了,習慣到20整一點哈哈哈
grd[x][i]=grd[grd[x][i-1]][i-1];//預處理grd陣列
for(i=head[x];i;i=nxt[i])
if(to[i]!=fa)
dfs(to[i],x);
}
int lca(int x,int y)
{
int i;
if(d[x]<d[y])
swap(x,y);
for(i=20;i>=0;i--)//從大到小列舉
if((1<<i)<=d[x]-d[y])//1<<i就是2^i,也可以預處理成陣列
x=grd[x][i];
if(x==y)
return x;//當y是x的祖先時
for(i=20;i>=0;i--)
if(grd[x][i]!=grd[y][i])
x=grd[x][i],y=grd[y][i];//一起往上跳
return grd[x][0];//返回父節點
}
int main()
{
n=read();m=read();s=read();
int i,j,x,y;
for(i=1;i<n;i++)
{
x=read();y=read();
add(x,y);
add(y,x);//鄰接表
}
dfs(s,0);//預處理
for(i=1;i<=m;i++)
{
x=read();y=read();
printf("%d\n",lca(x,y));
}
return 0;
}
當然,遇到相應的題可再加陣列,比如有時可定義dis[x][i]表示x節點向上跳2^i的距離(權),或者表示最大最小值等。
相關推薦
求LCA——最近公共祖先 倍增演算法
LCA是啥呢,LCA就是一棵樹裡兩個節點的最近公共祖先,如下圖 2號節點和3號節點的LCA就是1, 5號節點和11號節點的LCA就是2,8號節點和4號節點的lca就是4 那麼怎麼求LCA呢。首先要建樹,然後最容易想到的就是兩個節點一起向上跳,第一個相遇的節點就是LCA
LCA(最近公共祖先)倍增演算法
最近公共祖先有多種演算法 如倍增,RMQ,樹鏈剖分等 這裡先介紹倍增演算法 預處理複雜度nlog(n); 詢問複雜度log(n); 倍增與二進位制息息相關 與分塊的演算法有些相似之處 使用倍增演算法時開一個fa[n][S]陣列 fa[i][j]
倍增法求Lca(最近公共祖先)
一. 明確問題 看標題便知道了, 這篇部落格力求解決的問題是求出一棵樹的兩個結點的最近公共祖先(LCA), 方法是倍增法. 那麼什麼是Lca呢? 它是一棵樹上兩個結點向上移動, 最後交匯的第一個結點, 也就是說這兩個結點祖先裡離樹根最遠也是離
[筆記]LCA最近公共祖先---倍增在線算法
targe mdk 在線 com diy href mta 算法 collect 059M37853N虜3Jhttp://www.zcool.com.cn/collection/ZMTg2OTM5ODg=.html 痹o83RI世9EUS兩http://www.zcool
求LCA(最近公共祖先)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <
(轉)Tarjan應用:求割點/橋/縮點/強連通分量/雙連通分量/LCA(最近公共祖先)
應用 說明 lca ref father 無向圖 沒有 經理 遠的 本文轉載自:http://hi.baidu.com/lydrainbowcat/item/f8a5ac223e092b52c28d591c 作者提示:在閱讀本文之前,請確保您已經理解並掌握了基本的T
Tarjan離線演算法 (LCA最近公共祖先)
Tarjan離線演算法是利用並查集和DFS來達到離線處理的目的 我們都知道,對於一棵樹,後序遍歷一遍,訪問它的根的時機一定是後與它的孩子的。換一句話,當開始遍歷它的根節點的時候,它遍歷過的孩子的公共祖先一定是這個根而這也就成為了我們解題的思想。 由於是需要對
LCA最近公共祖先的離線演算法(Tarjan)和線上演算法(ST)
最近公共祖先簡稱LCA(Lowest Common Ancestors),所謂LCA,是當給定一個有根樹T時,對於任意兩個結點u、v,找到一個離根最遠的結點x,使得x同時是u和v的祖先,x 便是u、
[轉]LCA 最近公共祖先
log 比較 下一步 個人 idt 合並函數 orz ref tle 原文傳送門orzJVxie Tarjan(離線)算法的基本思路及其算法實現 首先是最近公共祖先的概念(什麽是最近公共祖先?): 在一棵沒有環的樹上,每個節點肯定有其父親節點和祖先節點
LCA 最近公共祖先 (模板)
ace memset air size oid int fin vector n) 1 #include <iostream> 2 #include <stdio.h> 3 #include <cstring> 4 #i
筆記:LCA最近公共祖先 Tarjan(離線)算法
否則 二叉樹 tail [] info enter 父親節 自己 初始 LCA最近公共祖先 Tarjan他賤(離線)算法的基本思路及其算法實現 本文是網絡資料整理或部分轉載或部分原創,參考文章如下: https://www.cnblogs.com/JVxie/p/4
近親結婚之 LCA 最近公共祖先
故事出真知 阿珍 愛上了 阿強 但 被他們的父母 拒絕 了 沒錯 狗血的近親結婚劇情開始了 阿珍 說: 不行,我深深愛著我的阿強,誰也不能把我們分開 阿強 深情地望著阿珍 說 一定會有機會的! 此時 他突然靈機一現 只要不是 三代近親 就可以了!!! 現在 深深相
LCA 最近公共祖先 模板
Lca 離線演算法(tarjan演算法) 模板 #include<bits/stdc++.h> #define _ 1000100 using namespace std; char buf[1<<15],*fs,*ft; inline char getc()
『圖論』LCA最近公共祖先
概述篇 LCA(Least Common Ancestors),即最近公共祖先,是指這樣的一個問題:在一棵有根樹中,找出某兩個節點 u 和 v 最近的公共祖先。 LCA可分為線上演算法與離線演算法 線上演算法:指程式可以以序列化的方式一個一個處理輸入,也就是說在一開始並不需要知道所有的輸入。
LCA 最近公共祖先,樹上兩點距離,線段重合長度
對於LCA的一些理解 RMQ dfs處理樹 對於一個樹形結構,可以用dfs將一顆樹轉化成陣列,陣列中記錄每個點的標號,這樣陣列就按照dfs的順序把樹存了下來 確定祖先的範圍 對於詢問的節點X和Y, X、Y的祖先一定存在於陣列中X、Y第一次出現的區間內,而且祖先就是區
LCA 最近公共祖先 tarjan離線 總結 結合3個例題
在網上找了一些對tarjan演算法解釋較好的文章 並加入了自己的理解 LCA(Least Common Ancestor),顧名思義,是指在一棵樹中,距離兩個點最近的兩者的公共節點。也就是說,在兩個點通往根的道路上,肯定會有公共的節點,我們就是要求找到公共的節點中,深
LCA最近公共祖先——Tarjan模板
tar wap 交換 最大 dfs %d scan 公共祖先 class LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根樹中,找出某兩個結點u和v最近的公共祖先。 Tarjan是一種離線算法,時間復雜度O(n+Q),Q表示詢問次數,其
『圖論』LCA 最近公共祖先
## **概述篇** LCA (Least Common Ancestors) ,即最近公共祖先,是指這樣的一個問題:在一棵有根樹中,找出某兩個節點 `u` 和 `v` 最近的公共祖先。 LCA 可分為**線上演算法**與**離線演算法** - **線上演算法:**指程式可以以序列化的方式一個一個
求最近公共祖先(LCA)的三種方法總結(Tarjan/倍增/樹鏈剖分)
以模板題目poj1330為例 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:
最近公共祖先(LCA) 【倍增演算法】
題目連結:洛谷-P3379 ## **題目描述**: 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 思路 這是一個裸的LCA問題,即求書上兩個節點的最近公共祖先。 我們可以用樹上倍增來做; 當然,在做之前我們假設不知道該演算法。那麼我們如何來做