HDU 5266 bc# 43 LCA+跳表

学了一发LCA的倍增算法+跳表维护。

先说说LCA倍增算法,思路是fa[i][j]求的是i结点的2^j倍的祖先,其中2^0就是父结点了。所以可以递推fa[i][j]=fa[fa[i][j-1]][j-1]。

当求LCA时,设深度u>v,则先倍增把u提到v的同等深度,若u==v,lca就是u,否则,两点同时倍增,直到最小深度的p[u][j]!=p[v][j],此时他们的父亲p[u][0]即lca。

可以看大牛http://www.cnblogs.com/OUSUO/p/3805715.html?utm_source=tuicool,先转一发。

1. DFS预处理出所有节点的深度和父节点
复制代码
inline void dfs(int u)
{
    int i;
    for(i=head[u];i!=-1;i=next[i])  
    {  
        if (!deep[to[i]])
        {            
            deep[to[i]] = deep[u]+1;
            p[to[i]][0] = u; //p[x][0]保存x的父节点为u;
            dfs(to[i]);
        }
    }
}
复制代码

2. 初始各个点的2^j祖先是谁 ,其中2^j(j=0...log(该点深度))倍祖先,1倍祖先就是父亲,2倍祖先是父亲的父亲......。

复制代码
void init()
{
    int i,j;
    //p[i][j]表示i结点的第2^j祖先
    for(j=1;(1<<j)<=n;j++)
        for(i=1;i<=n;i++)
            if(p[i][j-1]!=-1)
                p[i][j]=p[p[i][j-1]][j-1];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
}
复制代码
3.从深度大的节点上升至深度小的节点同层,如果此时两节点相同直接返回此节点,即lca。
否则,利用倍增法找到最小深度的p[a][j]!=p[b][j],此时他们的父亲p[a][0]即lca。
复制代码
int lca(int a,int b)//最近公共祖先
{
    int i,j;
    if(deep[a]<deep[b])swap(a,b);
    for(i=0;(1<<i)<=deep[a];i++);
    i--;
    //使a,b两点的深度相同
    for(j=i;j>=0;j--)
        if(deep[a]-(1<<j)>=deep[b])
            a=p[a][j];
    if(a==b)return a;
    //倍增法,每次向上进深度2^j,找到最近公共祖先的子结点
    for(j=i;j>=0;j--)
    {
        if(p[a][j]!=-1&&p[a][j]!=p[b][j])
        {
            a=p[a][j];
            b=p[b][j];
        }
    }
    return p[a][0];
}
复制代码
 
 
 
维护跳表的思想其实和ST算法是一样的,dp[i][j]表示区间i到i+(2^j)-1的LCA,由底往上递推就是dp[i][j]=LCA(dp[i][j-1],dp[i+(1<<j)][j-1])。即可。查询时,也按照跳表查询就可以了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

const int N=300010;

struct Edge{
	int v,next;
}edge[N*2];
int head[N],tot;
int fa[N][22],dp[N][22];
int dep[N];

void addedge(int u,int v){
	edge[tot].v=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}

void BFS(int rt){
	queue<int>que;
	que.push(rt);
	fa[rt][0]=-1; dep[rt]=1;
	while(!que.empty()){
		int u=que.front();
		que.pop();
		for(int i=1;i<=20;i++){
			if(fa[u][i-1]!=-1){
				fa[u][i]=fa[fa[u][i-1]][i-1];
			}
		}
		for(int e=head[u];e!=-1;e=edge[e].next){
			int v=edge[e].v;
			if(dep[v]==0){
				dep[v]=dep[u]+1;
				fa[v][0]=u;
				que.push(v);
			}
		}
	}
}

int LCA(int u,int v){
	int i,j;
	if(dep[u]<dep[v])swap(u,v);
	for(i=0;(1<<i)<=dep[u];i++);
	i--;
	for(j=i;j>=0;j--){
		if(dep[u]-(1<<j)>=dep[v])
		u=fa[u][j];
	}
	if(u==v) return u;
	for(j=i;j>=0;j--){
		if(fa[u][j]!=-1&&fa[u][j]!=fa[v][j]){
			u=fa[u][j];
			v=fa[v][j];
		}
	}
	return fa[u][0];
}

int main(){
	int n,q,u,v;
	while(scanf("%d",&n)!=EOF){
		memset(head,-1,sizeof(head));
		tot=0;
		memset(fa,-1,sizeof(fa));
		for(int i=1;i<n;i++){
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		memset(dep,0,sizeof(dep));
		BFS(1);
	//	cout<<LCA(1,5)<<endl;
	//	cout<<LCA(2,3)<<endl;
		for(int i=1;i<=n;i++)
		dp[i][0]=i;
		for(int j=1;j<=20;j++){
			for(int i=1;i+(1<<j)-1<=n;i++)
			dp[i][j]=LCA(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
		}
/*		for(int i=1;i<=n;i++){
			for(int j=0;j<=6;j++)
			cout<<dp[i][j]<<" ";
			cout<<endl;
		}*/
		scanf("%d",&q);
		while(q--){
			scanf("%d%d",&u,&v);
	//		if(u>v) swap(u,v);
			if(u==v)
			printf("%d
",u);
			else{
				int ans=u;
				for(int i=20;i>=0;i--){
					if(u+(1<<i)-1<=v){
						ans=LCA(ans,dp[u][i]);
						u=u+(1<<i);
					}
				}
				printf("%d
",ans);
			}
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/jie-dcai/p/4560174.html