这里,浅略得谈一下对倍增法的看法。
倍增法,很巧妙地运用了二进制的特点。将问题转化得更加方便。
公式
f[ i ][ j ]表示从i 开始,向上攀 2 ^ j 的距离的点;
先用O(n log n)的复杂度求出树中每一个结点的f[ ] 值;然后在询问的时候用O(log n)的时间查询两个点。
就是不断向上以 二的倍数数为 长度搜索。
具体的实现可以看代码。
<pre name="code" class="cpp">#include<cstdio>
#include<cstdlib>
#include<cstring>
#define maxn 88000
const int M=18;
int tot=0,to[maxn*2],next[maxn*2],s[maxn],deep[maxn];
int f[maxn][M+1];
inline void add(int x,int y){
to[++tot]=y;next[tot]=s[x];s[x]=tot;
to[++tot]=x;next[tot]=s[y];s[y]=tot;
}
void dfs(int x)
{
for(int e=s[x];e;e=next[e])
if(f[x][0]!=to[e]){
f[to[e]][0]=x;
deep[to[e]]=deep[x]+1;
dfs(to[e]);
}
}
inline void swap(int &x,int &y){x^=y;y^=x;x^=y;}
inline int find(int x,int y)
{
if(deep[x]<deep[y])swap(x,y);
int d=deep[x]-deep[y];//要善于利用二进制。
for(int i=M;i+1;i--)
if((1<<i)&d)x=f[x][i];
if(x==y)return x;
for(int i=M;i+1;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];y=f[y][i];
}
}
return f[x][0];
}
int main()
{
int n,m;scanf("%d",&n);
memset(s,0,sizeof(s));
memset(f,0,sizeof(f));
for(int i=1,x,y;i<=n-1;i++){
scanf("%d%d",&x,&y);
add(x,y);
}
deep[1]=1;dfs(1);
//预处理;(倍增);
for(int i=1;i<=M;i++)
for(int j=1;j<=n;j++)
{
f[j][i]=f[f[j][i-1]][i-1];
}
scanf("%d",&m);
for(int x,y;m;m--){
scanf("%d%d",&x,&y);
printf("%d\n",find(x,y));
}
// system("pause");
return 0;
}