倍增算法可以在線求樹上兩個點的LCA,時間復雜度為nlogn
預處理:通過dfs遍歷,記錄每個節點到根節點的距離dist[u],深度d[u]
init()求出樹上每個節點u的2^i祖先p[u][i]
求最近公共祖先,根據兩個節點的的深度,如不同,向上調整深度大的節點,使得兩個節點在同一層上,如果正好是祖先結束,否則,將連個節點同時上移,查詢最近公共祖先。
void dfs(int u){ for(int i=head[u];i!=-1;i=edge[i].next){ int to=edge[i].to; if(to==p[u][0])continue; d[to]=d[u]+1; dist[to]=dist[u]+edge[i].w; p[to][0]=u; //p[i][0]存i的父節點 dfs(to); } }
i的2^j祖先就是i的(2^(j-1))祖先的2^(j-1)祖先:
void init(){ for(int j=1;(1<<j)<=n;j++){ for(int i=1;i<=n;i++){ p[i][j]=p[p[i][j-1]][j-1]; } } }
LCA:
int lca(int a,int b){ if(d[a]>d[b])swap(a,b); //b在下面 int f=d[b]-d[a];//f是高度差 for(int i=0;(1<<i)<=f;i++){//(1<<i)&f找到f化為2進制后1的位置,移動到相應的位置 if((1<<i)&f)b=p[b][i];//比如f=5(101),先移動2^0祖先,然后再移動2^2祖先 } if(a!=b){ for(int i=(int)log2(N);i>=0;i--){ if(p[a][i]!=p[b][i]){//從最大祖先開始,判斷a,b祖先,是否相同 a=p[a][i]; b=p[b][i];//如不相同,a b同時向上移動2^j } } a=p[a][0];//這時a的father就是LCA } return a; }
