╭(′▽`)╯ide
总之,咱们都知道lca是啥,不须要任何基础也能想出来怎么用最暴力的方法求LCA,也就是深度深的点先跳到深度浅的点的同一深度,而后一块儿向上一步步跳。这样显然太慢了!spa
因此咱们要用倍增,倍增比较屌,直接2^k速度往上跳,并且复杂度和树剖lca差很少,那么步骤分为两步code
1.让两个点到同一深度blog
2.到了同一深度同步往上跳get
反正我一开始看的时候一直在想,万一跳过了怎么办?哈哈哈,因此说咱们有办法嘛:同步
定义deepv为v点的深度,设两个要求lca的点分别为a,b,且deepa >= deepbstring
因此,枚举找出最大的k使2^k <= deepa,这就是最大的跳的距离;io
接着让他们到达同一深度:event
从大到小枚举k,若是 deepa - 2^k >= deepb就往上跳2^k步,由于若是跳了2^k步的话必定deepa >= deepb模板
因此,咱们跳的第一步必定是能跳的最大的一步,因此接下来只能跳次大的一步,同理跳完以后deepa >= deepb
......
由于k是愈来愈小的,k = 0的时候2^k = 1,所以不管如何最后都会以最大的效率跳到相同的深度
如今跳到了相同的深度,而后要同时向上走找到lca。
假设跳了 2 ^ k步以后它们到的位置不相等,说明lca还在深度更浅的地方,由于若是跳以后到的位置相等了,显然这个位置必定在lca的上面
因此,只要判断跳了 2 ^ k步后它们的位置若是不相等,就跳这步,这样就保证了跳到的深度必定小于lca,最后k = 0时 2 ^ k = 1,
则枚举完了k,它们所在的深度显然必定是lca的深度-1,则lca就是它们任意一个的父亲。
代码(luogu lca模板):
#include <cstdio> #include <vector> #include <cstring> const int MaxN = 500010; int n,m,s; int par[MaxN][30]; int deep[MaxN]; bool vis[MaxN]; struct Edge{ int to,nxt; }e[MaxN*2]; int head[MaxN]; int cnt; void add(int u,int v){ e[++cnt].to = v; e[cnt].nxt = head[u]; head[u] = cnt; } void getdeep(int u){ vis[u] = 1; for(int i = head[u]; i; i = e[i].nxt){ int to = e[i].to; if(to == u || vis[to]) continue; par[to][0] = u; deep[to] = deep[u] + 1; getdeep(to); } } void getpar(){ for(int up = 1; (1<<up) <= n; up++){ for(int i = 1; i <= n ; i++){ par[i][up] = par[par[i][up-1]][up-1]; } } } int lca(int u,int v){ if(deep[u] < deep[v] ) std::swap(u,v); int max_jump = -1; while(1<<(max_jump+1) <= deep[u]) max_jump++; for(int i = max_jump; i >= 0; i--){ if(deep[u] - (1<<i) >= deep[v]){ u = par[u][i]; } } if(u == v) return u; for(int i = max_jump; i >= 0; i--){ if(par[u][i] != par[v][i]){ u = par[u][i]; v = par[v][i]; } } return par[u][0]; return 0; } int main() { scanf("%d%d%d",&n,&m,&s); for(int i = 1; i < n; i++ ){ int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); //par[v][0] = u; //par[u][0] = v; } deep[s] = 0; getdeep(s); getpar(); for(int i = 1; i <= m; i++){ int a,b; scanf("%d%d",&a,&b); printf("%d\n",lca(a,b)); } //par[i][j] = par[par[i][j-1]][j-1] return 0; }