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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

tarjan算法_【朝夕的ACM笔记】树上问题-最近公共祖先-倍增算法

發(fā)布時(shí)間:2025/3/19 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tarjan算法_【朝夕的ACM笔记】树上问题-最近公共祖先-倍增算法 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【朝夕的ACM筆記】目錄與索引

最近公共祖先-倍增算法

一、基本概念

最近公共祖先問題:對(duì)于給定的一顆有根樹,求其兩個(gè)節(jié)點(diǎn)的最近公共祖先。

祖先:節(jié)點(diǎn)本身、節(jié)點(diǎn)的父親、節(jié)點(diǎn)父親的父親……都是該節(jié)點(diǎn)的祖先。

公共祖先:兩個(gè)節(jié)點(diǎn)共同的祖先。

最近公共祖先(Lowest Common Ancestor)即LCA:距離根最遠(yuǎn)的公共祖先(也是深度最大的公共祖先)。

*LCA的重要性質(zhì):

①若

是 的祖先,則 。

②若

不互為祖先,則 分別在 兩棵不同的子樹上。

③兩點(diǎn)的LCA必然在兩點(diǎn)間的最短路上,事實(shí)上兩點(diǎn)間的最短路就是從其中一點(diǎn)到LCA再到另一點(diǎn)。故

LCA的常見求法有:倍增算法、Tarjan算法、ST表算法、樹鏈剖分四種。

本篇介紹第一種:倍增算法。

二、算法思想

2.1 樸素算法

在講倍增算法前,我們先考慮一下暴力的樸素算法。

首先我們知道

所以我們可以先找到深度比較大的那個(gè)點(diǎn),讓它先往上跳,直到兩點(diǎn)深度相同。

接著兩點(diǎn)一起往上跳,什么時(shí)候匯聚在一點(diǎn),那點(diǎn)就是最近公共祖先。

但是樸素算法的時(shí)間復(fù)雜度是比較大的,最壞情況下相當(dāng)于

。即每次詢問都要遍歷整棵樹。

2.2 倍增算法

倍增算法是樸素算法的改進(jìn)算法。對(duì)于每個(gè)點(diǎn),我們先求出它向上的第

個(gè)祖先。這樣再按照樸素算法向上跳時(shí),跳躍的次數(shù)會(huì)減少很多。

需要注意的是,向上跳躍時(shí),我們應(yīng)當(dāng)先考慮跳大的步子。

什么意思呢?

比如說(shuō)我們要求一個(gè)節(jié)點(diǎn)向上的第13個(gè)祖先。

那我們應(yīng)該先跳8步,再跳4步,再跳1步。

對(duì)于預(yù)處理:

我們?cè)O(shè)

為點(diǎn) 的第 個(gè)祖先。

則顯然

接下來(lái)從根節(jié)點(diǎn)向下DFS,每跑到一個(gè)點(diǎn),都可以求出這個(gè)點(diǎn)的前

個(gè)祖先。

倍增的轉(zhuǎn)移方程為

由于

有可能非整數(shù),不好處理,所以可以統(tǒng)一求 。

對(duì)于求解LCA:

第一步先統(tǒng)一深度:

假設(shè)

,則 。

直接向下取整就好了,最多多跳一步。

然后一起向上跳,從大的步子開始,直到匯聚在同一點(diǎn)。

時(shí)間復(fù)雜度:

倍增算法預(yù)處理的時(shí)間復(fù)雜度是

,包含詢問內(nèi)的最差復(fù)雜度為 。

三、參考代碼

#include<bits/stdc++.h> #define ll long long using namespace std; vector<int>e[500005]; int dep[500005]; int fa[500005][25]; int lg[500005]; void dfs(int s,int fn)//s表示當(dāng)前點(diǎn),fn是當(dāng)前點(diǎn)的父親節(jié)點(diǎn) {fa[s][0]=fn;//第一個(gè)祖先就是本身dep[s]=dep[fn]+1;//記錄點(diǎn)的深度f(wàn)or(int i=1;i<=lg[dep[s]]+1;i++)fa[s][i]=fa[fa[s][i-1]][i-1];//倍增轉(zhuǎn)移for(int i=0;i<e[s].size();i++)if(e[s][i]!=fn) dfs(e[s][i],s);//向下遍歷 } void pre(int n)//快速預(yù)處理log2 {lg[1]=0,lg[2]=1;for(int i=3;i<=n;i++) lg[i]=lg[i/2]+1; } int lca(int x,int y) {if(dep[x]<dep[y]) swap(x,y);//先保證x的深度大于等于ywhile(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]];//統(tǒng)一深度if(x==y) return x;//特判for(int i=lg[dep[x]];i>=0;i--)//從大步開始走if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];//一起往上跳return fa[x][0]; } int read()//快讀,增快讀入速度 {int re=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0' && ch<='9'){re=re*10+ch-'0';ch=getchar();}return re; } int main() {int n,m,s;//樹的點(diǎn)數(shù)、詢問次數(shù)、根節(jié)點(diǎn)n=read();m=read();s=read();for(int i=1;i<=n-1;i++){int x,y;x=read(),y=read();e[x].push_back(y);e[y].push_back(x);//樹是無(wú)向圖}pre(n);//預(yù)處理log2dfs(s,0);//dfs預(yù)處理fa數(shù)組for(int i=1;i<=m;i++){int a,b;a=read(),b=read();printf("%dn",lca(a,b));}return 0; }

總結(jié)

以上是生活随笔為你收集整理的tarjan算法_【朝夕的ACM笔记】树上问题-最近公共祖先-倍增算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。