給你樹的節點個數\(n\),詢問個數\(m\)和樹根\(s\),
輸入\(n,m,s\);
輸入\(n\)對\(x,y.\)表示\(x,y\)結點之間有一條邊
輸入\(m\)對\(a,b.\)表示求\(a,b\)的最近公共祖先git
下面的解釋均以這個圖爲例
今天用這道題來講一下倍增,先預處理出每一個節點的深度,和lg數組,就是\(log_2{i}\)的值.所謂倍增,就是按\(2\)的倍數來增大,也就是跳\(1,2,4,8,16,32……\)不過在這咱們不是按從小到大跳,而是從大向小跳,即按\(……32,16,8,4,2,11\)來跳,若是大的跳不過去,再把它調小。這是由於從小開始跳,可能會出現「悔棋」的現象。拿\(5\)爲例,從小向大跳,\(5≠1+2+4\),因此咱們還要回溯一步,而後才能得出\(5=1+4\);而從大向小跳,直接能夠得出\(5=4+1\)。這也能夠拿二進制爲例,\(5(101)\),從高位向低位填很簡單,若是填了這位以後比原數大了,那我就不填,這個過程是很好操做的。
仍是以\(17\)和\(18\)爲例(此例只演示倍增,並非倍增LCA算法的真正路徑)
\(17->3\)
\(18->5->3\)
能夠看出向上跳的次數大大減少。這個算法的時間複雜度爲\(O(nlogn)\),已經能夠知足大部分的需求。
想要實現這個算法,首先咱們要記錄各個點的深度和他們\(2^{i}\)級的的祖先,用數組\(depth\)表示每一個節點的深度,\(fa[i][j]\)表示節點\(i\)的\(2^j\)級祖先。 代碼以下:算法
void dfs(int f,int fath)//f表示當前節點,fath表示他的父親 { depth[f]=depth[fath]+1; fa[f][0]=fath; for(int i=1;(1<<i)<=depth[f];i++) fa[f][i]=fa[fa[f][i-1]][i-1]; //這個轉移是核心.意思是f的2^i祖先等於f的2^(i-1)祖先的2^(i-1)祖先 //2^i=2^(i-1)*2^(i-1) for(int i=head[f];i;i=e[i].nex) if(e[i].t!=fath) dfs(e[i].t,f); }
預處理完畢後,咱們就能夠去找它的\(LCA\)了,爲了讓它跑得快一些,咱們能夠加一個常數優化數組
for(int i=1;i<=n;i++) //預先算出log_2(i)+1的值,用的時候直接調用就能夠了 lg[i]=lg[i-1]+(1<<lg[i-1]==i); //看不懂的能夠手推一下
接下來就是倍增\(LCA\)了,咱們先把兩個點提到同一高度,再統一開始跳。
但咱們在跳的時候不能直接跳到它們的\(LCA\),由於這可能會誤判,好比\(4\)和\(8\),在跳的時候,咱們可能會認爲\(1\)是它們的\(LCA\),但\(1\)只是它們的祖先,它們的\(LCA\)實際上是\(3\)。因此咱們要跳到它們\(LCA\)的下面一層,好比\(4\)和\(8\),咱們就跳到\(4\)和\(5\),而後輸出它們的父節點,這樣就不會誤判了。優化
int lca(int x,int y) { if(depth[x]<depth[y])//保證x比y深.便於一會調到同一深度 swap(x,y); while(depth[x]>depth[y])//調到同一深度. x=fa[x][lg[depth[x]-depth[y]]-1]; if(x==y)//若是到了同一個點了,那就到了lca了. return x; for(int k=lg[depth[x]]-1;k>=0;k--)//不斷往上調. if(fa[x][k]!=fa[y][k])//若是兩個父親不同. x=fa[x][k],y=fa[y][k];//繼續往上跳 return fa[x][0];//返回 }
完整的求\(17\)和\(18\)的LCA的路徑:
\(17->10->7->3\)
\(18->16->8->5->3\)
解釋:首先,\(18\)要跳到和\(17\)深度相同,而後\(18\)和\(17\)一塊兒向上跳,一直跳到\(LCA\)的下一層(1\(7\)是\(7\),\(18\)是\(5\)),此時\(LCA\)就是它們的父親
這個題就是這樣.\(so\) \(easy\).
完整代碼spa
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<string> #include<algorithm> #include<iomanip> #include<cstdlib> #include<queue> #include<map> #include<set> #include<stack> #include<vector> #define ll long long using namespace std; inline int read() { int s=0,w=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();} while(isdigit(ch)) s=s*10+ch-'0',ch=getchar(); return s*w; } struct yyy { int t,nex; }e[2*500001]; int depth[500001],fa[500001][22],lg[500001],head[500001]; int tot; void add(int x,int y) { e[++tot].t=y; e[tot].nex=head[x]; head[x]=tot; } void dfs(int f,int fath) { depth[f]=depth[fath]+1; fa[f][0]=fath; for(int i=1;(1<<i)<=depth[f];i++) fa[f][i]=fa[fa[f][i-1]][i-1]; for(int i=head[f];i;i=e[i].nex) if(e[i].t!=fath) dfs(e[i].t,f); } int lca(int x,int y) { if(depth[x]<depth[y]) swap(x,y); while(depth[x]>depth[y]) x=fa[x][lg[depth[x]-depth[y]]-1]; if(x==y) return x; for(int k=lg[depth[x]]-1;k>=0;k--) if(fa[x][k]!=fa[y][k]) x=fa[x][k],y=fa[y][k]; return fa[x][0]; } int n,m,s; int main() { n=read(),m=read(),s=read(); for(int i=1,x,y;i<=n-1;i++) x=read(),y=read(),add(x,y),add(y,x); dfs(s,0); for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i); for(int i=1,x,y;i<=m;i++) x=read(),y=read(),printf("%d\n",lca(x,y)); return 0; }
倍增也就是這個思想了吧.3d