有空再学个trajan
--------------------------------------------------首先,LCA是干嘛的
这个lca呢,是用来求一棵树上任意两个点 的最近公共祖先,
说实话,画图比较好理解,但是,1我懒,2灵魂画手
公共祖先不用我说吧,,就是你从这两个点出发,向上层走,最近的那个公共点(大概,不知道我说明白没)
------------------------------------------------------然后,LCA原理
简单说,就是你先把这两个点划到同一层去,把层数更深的点向上提,然后这就是两个同一层的点了(辈分一样的)
这样,让这两个点同时向上走,走到第一个相同的点就是公共祖先了,简单不?(当然具体实现并不是完全这样的)
----------------------------------------------------来看下怎样操作的
首先呢,你得了解一下什么叫倍增,不难,看下例子
给一个数列
5 6 7 5 6 5 2 8 4
然后打一个表示一个区间的最小值
那么
首先是区间长度为2的(为1的就是原序列=。=)那么这样看,(i,i+1)(看得懂嘛,就是q【i】表示i到i+1的最小值)
5 6 5 5 5 2 2 4 4
然后是长度为4的,显然可以通过两个区间长度为2的合并来得到(啊,这个好简单的,我能不解释嘛)
5 5 5 5 2 2 2 4 4
然后继续
5 5 5 2 2 2 2 4 4
然后,本来应该继续的说(表还没打完),但是我懒=。=,写的这些就够解释原理了
假如你要查询区间(1,3),那么只需要查询(1,2)再加上(3,3)就可以了(可能你会这样想,其实也不错),
/*任何一个数都可以用二进制表示嘛,这样因为我把所有区间长度为 2的整数次方 的表打出来了
这样你查询哪个区间我都可以照表做了*/
其实这里并不是这样做的
而是查询(1,2)+(2,3)就是从左查询一个len,从右查询一个len(满足pow(2,len)<=l,最大的len,l是区间长度),中间重复的没关系,对我们要的区间最小值没影响
好了倍增讲到这里,其实就是一个打二进制表的过程
------------------------------然后我们看LCA是怎样实现的
嘛,通过前面讲的倍增来打一个表,anc【i】【j】表示点i的2的j次方祖先(注意是倍增这个思想,具体实现可不一样)
例如anc【5】【0】,就是说点5的父亲节点(2的零次方是1这个不用说)
首先将点划(移动)到同一层,怎样移动的,假设两个点相差k层,我们通过枚举二进制的每一位来凑出这个k
假设这棵树总共2的len层,那么,第i层的i可以用len位二进制表示,每一层都可以的
这样从len为开始枚举,假设这一位是1,看下可以不(大于不行,小于等于才可以),
不行就枚举下一位,这样最后肯定能枚举出k
然后让它们同时向上枚举祖先,还是二进制,假设,不假设了和上面一样的
枚举到公共祖先,这样就结束了
---------最后,LCA为啥快-----因为用了二进制(二进制就是快,不服你咬我啊)
(二进制log级的,笑、)
贴一个代码,另那个原理真的让我笑了半天
代码源自
#include
#include
#include
using namespace std;
int n,m,s;
int anc[500010][20],deep[500010];
struct E
{
int to,next;
} tree[1000010];
int last[500010],cnt;
void build(int u,int v)
{
tree[++cnt].to=v;
tree[cnt].next=last[u];
last[u]=cnt;
}
void swap(int &a,int &b)
{
int t=a;
a=b;
b=t;
}
void dfs(int now,int fa) //处理出深度和父亲
{
for(int i=last[now]; i; i=tree[i].next)
{
if(tree[i].to!=fa)
{
deep[tree[i].to]=deep[now]+1;
dfs(tree[i].to,now);
anc[tree[i].to][0]=now;
}
}
}
void ready() //处理出各路祖先
{
for(int j=1; (1<=0; i--)
if(deep[x]-(1<=deep[y])
x=anc[x][i]; //判断看能否直接返回
if(x==y)
return x;
for(int i=maxlog; i>=0; i--)
if(anc[x][i]!=anc[y][i])
{
x=anc[x][i];
y=anc[y][i];
}
return anc[x][0];//返回父亲
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d%d",&n,&m,&s);
for(int i=1; i<=n-1; ++i)
{
int u,v;
scanf("%d%d",&u,&v);
build(u,v);
build(v,u);
}
dfs(s,s);
ready();
anc[s][0]=s;
for(int i=1; i<=m; ++i)
{
int a,b;
scanf("%d%d",&a,&b);
int ans=lca(a,b);
printf("%d\n",ans);//在线回答
}
return 0;
}