zoukankan      html  css  js  c++  java
  • 倍增LCA学习笔记

    前言

    ​ “倍增”,作为一种二进制拆分思想,广泛用于各中算法,如$ST$表,求解$LCA$等等...今天,我们仅讨论用该思想来求解树上两个节点的$LCA$(最近公共祖先)

    “倍增”是什么东西?

    ​ 倍增就是“成倍增加”的意思,比如$1$倍增后变成了$2$,$2$倍增后就变成了$4$,$4$变成$8$,以此类推...

    实现

    一直向上LCA

    ​ 在讲真正的倍增之前,我们先来说说最朴素的$LCA$,对于需要求解的两个点$(x,y)$,我们最先能想到的方法就是两个点先到达同一深度,然后一直往上跳父亲,知道两个点跳到同一个点上,这个点就是$LCA$。

    int LCA (int x, int y) {
        if (depth[x] < depth[y]) swap(x, y);
        while(depth[x] != depth[y]) x = fa[x];
    	while(x != y) x = fa[x], y = fa[y];
    	return x;
    }
    

    ​ 不难发现,这种算法的时间开销很大,我们想办法来优化它。

    倍增LCA

    ​ 就如同$ST$表一样,我们不妨设$f[i][j]$表示树上编号为$i$的节点向上跳$2^j$个节点后所达到的节点,如同$ST$表的预处理,我们很容易发现如何预处理出这个$f$数组:

    f[i][j] = f[f[i][j-1]][j-1];
    

    ​ 显然,$i$往上跳$2{j-1}$次之后再跳$2{j-1}$次之后就相当于$i$往上跳$2^j$次,我们可以借此来优化,利用二进制优化背包的思想那样,将跳的次数二进制拆分。

    ​ 于是,我们改写一下之前的代码

    int LCA (int x, int y) {
        if (depth[x] < depth[y]) swap(x, y);
        for (int i = LogN; i >= 0; --i)
            if (depth[f[x][i]] >= depth[y])
                x = f[x][i];
        if (x == y) return x;
        for (int i = LogN; i >= 0; --i)
            if (f[x][i] != f[y][i])
                x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    

    ​ 这样一来,速度就快很多了,由原来的$O(Depth)$变成了现在的$O(log_2(Depth))$

    代码

    #include <cstdio>
    #include <cstring>
    typedef int ll;
    
    const ll N = 5e5 + 10, M = 5e5 + 10, LogN = 25;
    ll n, m, s, depth[N], f[N][LogN], a, b, c;
    ll from[N], to[M << 1], nxt[M << 1], cnt, tmp, Log[N];
    inline void swap (ll &a, ll &b) {tmp = a, a = b, b = tmp;}
    //链式前向星加边
    void addEdge (ll u, ll v) {
    	to[++cnt] = v, nxt[cnt] = from[u], from[u] = cnt;
    }
    //计算深度&计算祖先
    void doit (ll u, ll fa) {
    	depth[u] = depth[fa] + 1;
    	for (register ll i = 1; i <= Log[n]; ++i) {
    		if ((1 << i) >= depth[u]) break;
    		f[u][i] =  f[f[u][i - 1]][i - 1];
    	}
    	for (register ll i = from[u]; i; i = nxt[i]) {
    		ll v = to[i];
    		if (v == fa) continue;
    		f[v][0] = u;
    		doit (v, u);
    	}
    }
    //计算LCA
    inline ll LCA (ll x, ll y) {
    	if (depth[x] < depth[y]) swap(x, y);
        //我们默认x为更深的那个点
    	for (register ll i = 0; i <= Log[n]; ++i)
            if (depth[f[x][i]] >= depth[y])
                x = f[x][i];
        //将x跳到和y同一深度上
    	if (x == y) return x;
    	for (register ll i = Log[n]; i >= 0; --i)
    		if (f[x][i] != f[y][i])
    			x = f[x][i], y = f[y][i];
        //一起向上跳
    	return f[x][0];
        //不难看出,此时两个点均在其LCA的下方,往上跳一次即可
    }
    
    int main () {
    	scanf ("%d%d", &n, &m);//n节点数 m询问次数
    	Log[0] = -1;
    	for (register ll i = 1, u, v; i < n; ++i) {
    		scanf ("%d%d", &u, &v);
    		addEdge (u, v); addEdge(v, u);
    		Log[i] = Log[i >> 1] + 1;
    	}
    	Log[n] = Log[n >> 1] + 1;
    	doit (1, 0);
    	while (m--) {
    		scanf ("%d%d", &a, &b);
    		printf ("%d
    ", LCA(a, b)));
    	}
    	return 0;
    } 
    
  • 相关阅读:
    Linux下python2.7安装pip
    图片转tfrecords
    Python+argparse+notebook
    Pycharm缺少环境变量+无法获取libcudnn.so.6
    Python手动实现k-means
    手动实现二值化
    Tensorflow+InternalError: Blas GEMM launch failed
    tensorflow-gpu+"Failed to create session"
    Ubuntu16.04+wineQQ+解决版本过低
    /usr/lib/nvidia-384/libEGL.so.1 is not a symbolic link
  • 原文地址:https://www.cnblogs.com/water-mi/p/9794827.html
Copyright © 2011-2022 走看看