zoukankan      html  css  js  c++  java
  • 树上倍增一些理解和写法

    树上倍增可以比较容易求得i节点的第k个父亲,我们定义一个二维数组fa[i][j]代表节点i的第2^j个父亲,关于有什么用我们等会再说,现在先学会怎么去求这个fa数组

    我们可以通过从根节点开始一遍dfs求得所有fa数组,首先我们发现fa数组有这样一个特性,fa[i][j] = fa[ fa[i][j-1] ][j-1],什么意思呢,就是说节点i的第2^j个父亲就是i的第2^(j-1)个父亲的第2^(j-1)个父亲,这看起来似乎是一句显然成立的废话,但我们可以通过这个特性以O(nlogn)的复杂度内求fa数组。

    我们知道dfs是从根开始,一步一步往下走的,这就说明除了根节点,每个节点当它被dfs到的时候,它的所有父亲肯定都已经被dfs过了,这样通过刚才的关系,就可以由i的第2^(j-1)个父亲求fa[i][j]了,我们需要处理的只是i的第2^j个父亲有可能不存在罢了。我们初始化fa数组为0,然后从根开始dfs。若有n个节点,则显然一个节点最多只可能往前找2^log(n)个父亲

    void dfs(int x)  
    {  
        for(int i=1;i<=logn;i++)  //logn代表log(n)
            if(fa[x][i-1])   //在dfs(x)之前,x的父亲们的fa数组都已经计算过了
                fa[x][i]=fa[fa[x][i-1]][i-1];  
            else break;    //x的第2^j个父亲可能不存在
        for(/*每一个与x相连的节点i*/)  
            if(i!=fa[x][0])     //如果i是x的儿子
            {  
                fa[i][0]=x;       //记录儿子的第一个父亲是x  
                dep[i]=dep[x]+1;      //深度  
                dfs(i);  
            }  
    }  

    求出fa数组后有什么用呢?其实我们可以轻易的在O(logk)内求出i的第k个父亲,根据数组定义看起来我们只能求i的第2,4,8...2^j次方个父亲,实际上我们可以求出i的第任意个父亲

    我们有这样一个事实,任何一个数,都可以写成多个2的x次方的和,实际上这就是2进制转10进制的过程,比如:10的二进制表示为1010,它的左数第2位和第4位上是1,所以2^(2-1)+2^(4-1) = 2^1 + 2^3 = 10。这个思想很有用,需要记住

    所以对于任意一个数k,只要我们把它写成若干个2的次方形式的数的和,就可以利用fa数组了。

    另外我们代码中还有个技巧,(1<<i)&k表示k的二进制表示中,左数第(i-1)位上是否为1 

    经过上面知识我们可以写出求i的第k个父亲的代码

    int father(int i,int k)  
    {  
        for(int x=0;x<=int(log2(k));x++)  
            if((1<<x)&k)    
                i=fa[i][x];     
        return i;  
    }  
  • 相关阅读:
    PS选区认识
    移动工具
    PS认识及新建文件
    第02组 Alpha冲刺(3/4)
    第02组 Alpha冲刺(2/4)
    第02组 Alpha冲刺(1/4)
    第02组 团队Git现场编程实战
    第二次结对编程作业
    团队项目-需求分析报告
    团队项目-需求分析报告
  • 原文地址:https://www.cnblogs.com/chaoswr/p/8489224.html
Copyright © 2011-2022 走看看