zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    Kubernetes 再深入一点点
    10分钟完成 mongodb replSet 部署
    网络篇
    p2p 打洞专场(转)
    Dockerfile 编写
    关于github 代码管理,协作开发
    Kubernetes 基于 ubuntu18.04 手工部署 (k8s)
    备忘 ubuntu ip 及 dns 的坑
    各种语言web性能简单对比测试
    vue 按需加载
  • 原文地址:https://www.cnblogs.com/jie-dcai/p/4560174.html
Copyright © 2011-2022 走看看