来源:https://www.luogu.org/problem/show?pid=3379#sub
洛谷P3379
数据范围很大,存储树的方法:
head[i]表示节点i的头指针,指向与其相连的一条边。
e[i]用来存储边的信息,to表示其指向的节点,next表示u节点的上一条边的编号。
倍增思想。
代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <cstdio>
#include <string>
#include <vector>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
const int maxn=5e5+10;
const int maxh=19;
int anc[maxn][maxh]={0},dep[maxn]={0};
struct data{int to,next;}e[maxn*2];
int head[maxn]={0};
int cnt=0,s[maxn]={0};
void ins(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int root)
{
int top=0;
s[++top]=root;
for(int i=0;i<maxh;i++)anc[root][i]=root;
while(top)
{
int x=s[top--];
if(x!=root)for(int i=1;i<maxh;i++)anc[x][i]=anc[anc[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(v!=anc[x][0])
{
anc[v][0]=x;
dep[v]=dep[x]+1;
s[++top]=v;
}
}
}
}
int swim(int x,int h)
{
for(int i=0;h>0;i++)
{
if(h&1)x=anc[x][i];
h=(h>>1);
}
return x;
}
int lca(int x,int y)
{
if(dep[x]>dep[y])swap(x,y);
y=swim(y,dep[y]-dep[x]);
if(x==y)return x;
while(1)
{
int pos;
for(pos=0;anc[x][pos]!=anc[y][pos];pos++);
if(pos==0)return anc[x][0];
x=anc[x][pos-1];
y=anc[y][pos-1];
}
return -1;
}
int main()
{
int n,m,s;
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(s);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
return 0;
}