基本思想:(參考:from lanshui_yang)
deep[i] 表示 i節點的深度, fa[i,j]表示 i 的 2^j (即2的j次方) 倍祖先,那麼fa[i , 0]即為節點i 的父親,然後就有乙個遞推式子:
fa[i,j]= fa [ fa [i,j-1] , j-1 ]
可以這樣理解:
設tmp = fa [i, j - 1] ,tmp2 = fa [tmp, j - 1 ] ,即tmp 是i 的第2 ^ (j - 1) 倍祖先,tmp2 是tmp 的第2 ^ (j - 1) 倍祖先 , 所以tmp2 是i 的第 2 ^ (j - 1) + 2 ^ (j - 1) = 2^ j 倍祖先,注意:這裡的「倍」可不能理解為倍數的意思,而是距離節點
i有多遠的意思,節點i的第
2 ^ j
倍祖先表示的節點u滿足
deep[ u ] - deep[ i ] = 2 ^ j
。這樣子乙個o(nlogn)的預處理求出每個節點的 2^k 的祖先
然後對於每乙個詢問的點對a, b的最近公共祖先就是:
先判斷是否 d[x]< d[y] ,如果是的話就交換一下(保證 x 的深度大於 y 的深度), 然後把 x 調到與 y 同深度, 同深度以後再把a, b 同時往上調,調到有乙個最小的 j 滿足fa [x,j] != fa [y,j] (x,y是在不斷更新的), 最後再把(x,y)往上調(x=p[x,0], y=p[y,0]) ,乙個乙個向上調直到x = y, 這時 x或y 就是他們的最近公共祖先。
ps:如果還是不明白,就手動模擬一棵節點數為9的樹(如下圖所示),很快就會理解的。還有我不得不感嘆一句 :二進位制真的很神奇!!
}}void bz() // 倍增祖先
}}void swap(int &x , int &y)
int lca(int u , int v)
}if(u == v) return u ;
for(i = m - 1 ; i >= 0 ; i --)
}u = fa[u][0] ;
return u ;
}void init()
}deep[root] = 1 ;
dfs(root) ;
bz() ;
int u , v ;
scanf("%d%d" , &u , &v) ;
printf("%d\n", lca(u , v)) ;
}int main()
return 0 ;
}
倍增LCA複習
時間過去了如此之久,我連倍增lca都不怎麼記得了,要粗事啊。首先預處理層數和每個節點的父親,然後預處理p陣列,p i,j 表示i向上第2 j個祖先。最後對於每個詢問x,y先把x,y變成同一層數的 x或y向上走直到兩個層數相等 然後x,y同時向上走,直到x和y的父親相同位置。自 1.dfs預處理出所有...
(倍增)假期旅行
一道倍增題。設f i j 表示從i位置開始,走2 j到的最遠城市。考慮每乙個座位的限定,對於一段 l,r 如果a位置為空,那麼 l,r 至少能到r位置,對於預定座位的條件,按座位為第一關鍵字排序之後,維護乙個城市單調的東東,線段樹可做。includeusing namespace std const...
倍增LCA模板
注意!本篇題解不適合初學lca的同學學習,因為我講的很爛很不清楚。倍增,顧名思義,就是成倍增加的意思。我們知道,任何乙個數字都可以表示成二進位制。那麼對於一條長度為n的鏈,我們總是可以跳大概logn次到達最後。對於鏈上任意一點,我們都可以在大概logn的複雜度下詢問到,其實倍增的思路就是二分,和快速...