zoukankan      html  css  js  c++  java
  • 树上倍增

    倍增的思是二进制,二进制需要进行位运算
            首先开一个n×logn的数组,比如fa[n][logn],其中fa[i][j]表示i节点的第2^j个父亲是谁。

    然后,我们会发现有这么一个性质:   fa[i][j]=fa [fa[i][j-1]] [j-1]

    用文字叙述为:i的第2^j个父亲 是 i的第2^(j-1)个父亲的第2^(j-1)个父亲。

    个人理解这个性质 认为 : 2^j = 2^(j-1) + 2^(j-1)   

    求 i 的第K个父亲  需要将K拆分分成多个满足 2^n 的数相加

    int father(int i,int k)
    {
        for(int x=0;x<=int(log2(k));x++)
            if((1<<x)&k)    //(1<<x)&k可以判断k的二进制表示中,第x位上是否为1  ,1左移x位与K的x位 进行与(&)计算。
    i=fa[i][x]; //把i往上 加一个k满足的2^n, 当循环结束,所有的2^n都加上,即找到了第K个父亲。 return i; }

    用树上倍增的时候,用dfs预处理出 每一个数的所有的父节点,即fa[n][logn] 数组,便于后面查询父节点。

    如果待处理的树有n个节点,那么最多有一个节点会有2^(logn)个父亲,所以我们的fa数组第二维开logn就够了。

    (depth[i]表示i的深度,这个可以一起处理出来,以后要用)

    //nowp=nowposition  fa=father 
    void dfs(int nowp,int fa){
        depth[nowp] =depth[fa]+1; 
        father[nowp][0]=fa;
        for(int j=1;j <=lg[depth[nowp]]+1;++j)
            father[nowp][j]=father [father[nowp][j-1]][j-1];
        for(int i=0;i<G[nowp].size();++i){
            if(G[nowp][i]!=fa)
                dfs(G[nowp][i],nowp); 
        } 
    }

    这样,我们在nlogn的时间内可以通过一遍dfs处理出这棵树的相关信息。 倍增的应用中,最基础的应该就是求LCA(最近公共祖先),时间复杂度是logn。

    对于求u、v的LCA,我们可以先把u、v用倍增法把深度大的提到和另一个深度相同。如果此时u、v已经相等了,表示原来u、v就在一条树链上,直接返回此时的结果。

            如果此时u、v深度相同但不等,则证明他们的lca在更“浅”的地方,此时需要把u、v一起用倍增法上提到他们的父亲相等。为啥是提到父亲相等呢?因为倍增法是一次上提很多,所以有可能提“过”了,如果是判断他们本身作为循环终止条件,就无法判断是否提得过多了,所以要判断他们父亲是否相等。

    int LCA(int u,int v){
        if(depth[u]<depth [v]) swap(u,v);
    //int dc = depth[u]-depth[v];
    //    for(int j=0;j<maxbit;++j)
    //    {
    //        if((dc>>j)&1) u=father[u][j];
    //    }
        while(depth[u]!=depth[v]){
            u=father[u][lg[depth[u]-depth[v]]];
        }
        if(u ==v) return u;
        for(int j= lg[depth[u]];j>=0;--j){
            if(father[u][j]!=father[v][j]){
                u=father[u][j];
                v=father[v][j];
            }
            
        }
        return father[u][0]; 
    } 

    树上倍增的真正意义

    例题: 给你一棵树和两个点x和y,求这两点间路径的路径最大/小的点权/边权

    我们可以设dis[i][j] 表示i到他的第2^j 个祖先的路径最大值,就可以边求LCA 边顺便求出两点距离

    还有求两点的路径上路径权值和呢?

    设sum[i][j] 表示i到他的第2^j个祖先的路径权值和,同时也可边求LCA边求和,

    因为 sum[i][j]=sum[i][j−1]+sum[anc[i][j−1]][j−1]) 

    像RMQ求LCA ,是无法解决这类问题的

    参考链接:

    https://blog.csdn.net/saramanda/article/details/54963914

    https://blog.csdn.net/enjoy_pascal/article/details/78277008

     求LCA的纯模板 洛谷3379

    #include <iostream> 
    #include <vector>
    using namespace std;
    const int maxn =5e5+5;
    const int maxbit =32;
    vector<int> G[maxn];
    int depth[maxn],lg[maxn],father[maxn][maxbit];
    
    //nowp=nowposition  fa=father 
    void dfs(int nowp,int fa){
        depth[nowp] =depth[fa]+1; 
        father[nowp][0]=fa;
        for(int j=1;j <=lg[depth[nowp]]+1;++j)
            father[nowp][j]=father [father[nowp][j-1]][j-1];
        for(int i=0;i<G[nowp].size();++i){
            if(G[nowp][i]!=fa)
                dfs(G[nowp][i],nowp); 
        } 
    }
    
    int LCA(int u,int v){
        if(depth[u]<depth [v]) swap(u,v);
    //int dc = depth[u]-depth[v];
    //    for(int j=0;j<maxbit;++j)
    //    {
    //        if((dc>>j)&1) u=father[u][j];
    //    }
        while(depth[u]!=depth[v]){
            u=father[u][lg[depth[u]-depth[v]]];
        }
        if(u ==v) return u;
        for(int j= lg[depth[u]];j>=0;--j){
            if(father[u][j]!=father[v][j]){
                u=father[u][j];
                v=father[v][j];
            }
            
        }
        return father[u][0]; 
    } 
     
    int main ()
    {
    //    std::ios::sync_with_stdio(flase);
        lg[0]=-1; 
        for(int i=1; i<maxn;++i)
        { 
            lg[i]=lg[i>>1]+1;  //lg2n 向下取整。 
        }
        int n,m,s;
        scanf("%d%d%d",&n,&m,&s);
        int x,y;
        for(int i=1; i<= n-1;i++)
        {
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        } 
        dfs(s,0);
        while(m--)
        {
            scanf("%d%d",&x,&y);
            printf("%d
    ",LCA(x,y));
        }
        return 0;
    } 
    View Code

     参考 视频链接:https://www.bilibili.com/video/av41067872?p=2

  • 相关阅读:
    55_Go基础_1_22 map 基本用法
    59_Go基础_1_26 字符串
    56_Go基础_1_23 map的遍历与排序
    52_Go基础_1_19 切片的再扩容
    数据类型和Json格式
    MySQL 存储过程的三个例子
    struts整合spring
    Android动态操作RelativeLayout里面的Button
    windows环境变量的初步研究
    struts2源码分析StrutsPrepareAndExecuteFilter
  • 原文地址:https://www.cnblogs.com/young-children/p/11973476.html
Copyright © 2011-2022 走看看