zoukankan      html  css  js  c++  java
  • [笔记]倍增求LCA

    [笔记]倍增求LCA

    原题链

    算法描述

    LCA是指两个点的最近公共祖先.

    在程序中设f[x][k]表示x2^k 辈祖先,即从x向上(根节点)走 2^k 步所到达的节点,如果向上走到的节点不存在则令f[x][k] = 0,并且f[x][0]为x节点的父节点,因为 2^0 = 1,且任意一个节点向上走一步到达的就是它的父节点.同时我们会发现一个性质:∀k∈[1,log(n)],f[x][k] = f[f[x][k - 1][k - 1]],这个式子的意思是x向上跳 2^k 步所到达的节点与x向上跳 2^(k-1) 步所到达的节点再向上跳 2^(k-1) 步所到达的节点是相同的,可以用同底数幂相加的原理证明(a^b + a^c = a^(b+c)).以上为预处理部分.


    如何求LCA:设dis[x]为x节点的深度,并规定dis[x] ≥ dis[y]如果不满足这个条件则交换x,y;此时我们保证了x的深度一定大于y,所以我们先尝试将x向上跳 2^(logn) 步,…跳 2^1 步,跳 2^0 步,并检查所跳到的节点是否仍比y深,如果仍比y深则继续上跳.如果调到x==y则说明x,y已经到达了两者的LCA处,直接输出.但如果当两者的深度一致时x,y仍不相等,此时将x,y同时上跳直到f[x][i] != f[y][i],由于我们跳的步幅是从大到小的,所以一开始跳可能回调到两点的lca的祖先节点,这很明显不是答案,所以我们要缩小步伐,缩小到什么时候呢?应该是第一次两点向上跳到的节点不是同一个的时候,我们设这个节点为p,此时p节点的父节点(这个父节点不是通常意义上的高一级的直属父亲,而是倍增意义下跳 2^i 步到达的节点)即为x,y的LCA.算法结束,具体见程序

    AC代码

    #include <bits/stdc++.h>
    using namespace std;
    struct node{
    	int to,next;
    }edge[1000010];
    int fir[1000010],n,m,s,tot,t,dis[500010],f[500010][35];
    void add(int x,int y){//前向星存边
    	tot++;
    	edge[tot].to = y;
    	edge[tot].next = fir[x];
    	fir[x] = tot;
    }
    void bfs(){//预处理出每个点的深度,并求出f数组
    	memset(dis,0,sizeof(dis));
    	queue < int > q;
    	while(!q.empty())q.pop();
    	dis[s] = 1;
    	q.push(s);
    	while(!q.empty()){
    		int x = q.front();
    		q.pop();
    		for(int i = fir[x];i;i = edge[i].next){
    			if(dis[edge[i].to] != 0)continue;
    			f[edge[i].to][0] = x;
    			dis[edge[i].to] = dis[x] + 1;
    			for(int j = 1;j <= t;j++){
    				f[edge[i].to][j] = f[f[edge[i].to][j - 1]][j - 1];
    			}
    			q.push(edge[i].to);
    		}
    	}
    	return;
    }
    int lca(int x,int y){
    	if(dis[x] < dis[y])swap(x,y);
    	if(x == y)return x;
    	for(int i = t;i >= 0;i--){
    		if(dis[f[x][i]] >= dis[y])x = f[x][i];
    	}
    	if(x == y)return x;
    	for(int i = t;i >= 0;i--){
    		if(f[x][i] != f[y][i]){
    			x = f[x][i];
    			y = f[y][i];
    		}
    	}
    	return f[x][0];
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i = 1;i < n;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	t = (int)(log(n) / log(2)) + 1;
    	bfs();
    	for(int i = 1;i <= m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		printf("%d
    ",lca(x,y));
    	}
    	return 0;
    }
    

    结束

  • 相关阅读:
    已经连接到空闲例程的解决方法
    SQLplus命令中导出数据
    Oracle设置时间格式
    Oracle热备份
    多元化控制文件
    ORA28000: the account is locked
    Windows 7 如何删除 Windows.old 文件夹
    Linux常用命令之文件命令
    Linux 常用命令之进程管理
    Python中的apply,filter和map函数
  • 原文地址:https://www.cnblogs.com/czy--blog/p/13546210.html
Copyright © 2011-2022 走看看