板子(反正我只是个垃圾)
//树上倍增求LCA(RMQ),其实就是个简单的二进制拆分问题 //正确性很容易证明,不证了 //思想:eg: 求x节点和y节点的Lca,比较 x 与 y 节点的深度,将树中深的节点跳到 //与浅的节点相同深度(由大到小循环),如果刚好为深度浅的节点,那么就找到了,否则一起向 //上跳,找到最小的j使得他们所跳的位置不同,但父节点是相同的,返回所跳位置的父节点就好了 #include<bits/stdc++.h>//板子 using namespace std; const int maxn = 1000010; int head[maxn]; int deep[maxn];//节点深度 struct{ int v,net; }edge[maxn];//链式前向星(ORZ,向大佬低头) int n,m,root,cnt; int f[maxn][21];//每个当前节点的第2 ^ j次方是哪个节点 inline void add_edge(int x,int y) { edge[cnt].v = y; edge[cnt].net = head[x]; head[x] = cnt++; }//加边 void dfs(int cur) { for(int i=head[cur];i!=-1;i=edge[i].net) { if(!deep[edge[i].v])//如果节点未被访问过 { deep[edge[i].v] = deep[cur] + 1; f[edge[i].v][0] = cur;//父节点为cur dfs(edge[i].v); } } }//求节点的深度 void PRE() { for(int i=1;i<=19;++i) for(int j=1;j<=n;++j)//n个节点预处理 f[j][i] = f[f[j][i-1]][i-1]; //拆分二进制 }//预处理 int LCA(int x,int y) { if(deep[x] < deep[y]) swap(x,y); for(int i=19;i>=0;--i) { if(deep[f[x][i]] >= deep[y])//上升到同一deep x = f[x][i]; } if(x == y) return x; //否则查找最小上升的 for(int i=19;i>=0;--i) { if(f[x][i]!=f[y][i]) { x = f[x][i]; y = f[y][i]; } } return f[x][0]; } int main() { ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); memset(head,-1,sizeof(head));//边集起点为 u 第一次出现的位置 cin>>n>>m>>root; cnt = 1; int u,v; for(int i=1;i!=n;++i)//树有n-1条边,且为双向,因为得求深度,还得向上跳 { cin>>u>>v; add_edge(u,v); add_edge(v,u); } deep[root] = 1;//根的深度为1 dfs(root); PRE(); for(int i=1;i<=m;++i) { cin>>u>>v; cout<<LCA(u,v)<<'\n'; } }
来源:博客园
作者:chengyulala
链接:https://www.cnblogs.com/newstartCY/p/11618941.html