• 树上倍增 x



    树上倍增.

    dfs序的做法:

    思路:

    //f[i][j]表示dfs序中序号为i到序号为j的点之间深度最小的点的编号 
    dfs序[]//存0-...(id)编号 
    节点[]//存dfs序中所经过的节点号 
    dep[]//存走过的点所对应的点的深度 
    i : 1 2 3 4 5 6 7 ......
    id[i] : (该点在节点编号中第一次出现的位置所对应的dfs序编号) 
    //ans表示求u节点到i节点的最短距离... 
    ans=f[id[u]][id[v]];

    至于代码……额……没写

    树上倍增还能够用来求LCA

    其中f[i,j]表示i的第2^j祖先dfs预处理f[i,j]=f[f[i,j-1],j-1];

    对于每一对x,y先将深度调成一样再枚举j逐一往上找,这两个过程都是log的

    代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    //maybe my English is not very good 
    
    using namespace std;
    
    const int M = 5e5 + 1;
    int n,m,s;
    int num; 
    int deep[M],h[M];
    bool vs[M];
    int jumps[M][21];
    int p;
    
    struct A{
        int next;
        int to;
    }t[M<<1];
    
    inline int read() //optimize
    {
        int x=0,f=1;char ch=getchar();
    
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') f=-1;
            ch=getchar();
        }
    
        while(ch>='0'&&ch<='9')
        {
            x=x*10+ch-'0';
            ch=getchar();
        }
    
        return x*f;
    }
    
    void ADD(int x,int y) //connect the x and the y
    {
        num++;
        t[num].to=y;
        t[num].next=h[x];
        h[x]=num;
    }
    
    void Dfs(int u)
    {
        for(int i=h[u];i!=-1;i=t[i].next)
        {
            int v=t[i].to; //u's next side
            if(deep[v] == 0) //if v is not visited
            {
                deep[v]=deep[u]+1; //deep+1
                jumps[v][0]=u; //u is v's dad
                Dfs(v); //continue Dfs            
            }
        }
    }
    
    void steps()
    {
        p=int(log(n)/log(2)+0.001); //find the biggest
        for(int i=1;i<=p;i++) //the Limit
            for(int j=1;j<=n;j++)
                jumps[j][i]=jumps[jumps[j][i-1]][i-1];
    //the j jump 2^i can get to the (first jump 2^(i-1),then jump 2^i-1 can get to)
    //eh...I will speak in Chinese.
    //because 倍增 is use 次方的形式 increase
    }
    
    int LCA(int a,int b)
    {
        //We let the b's deep is small
        if(deep[a]<deep[b]) swap(a,b);
        for(int i=p;i>=0;i--)
        {//first let the a jump to the b's deep
            if(deep[jumps[a][i]]>=deep[b])
            a=jumps[a][i];
        }
        if(a == b) return b; //if the b is them's LCA , return b
        for(int i=p;i>=0;i--) //jump together
        {
            if(jumps[a][i]!=jumps[b][i])
            a=jumps[a][i],b=jumps[b][i]; //update
        }
        return jumps[a][0]; 
    }
    
    int main()
    {
        //s is the root
        n=read();m=read();s=read();
        for(int i=1;i<=n;i++) h[i]=-1;
        int x,y;
        for(int i=1;i<n;i++)
        {
            x=read();y=read();
            //connect the x and the y
            ADD(x,y);
            ADD(y,x);
        }
        deep[s]=1; //this is too important !!!
        //if you don't think so ,"//" it. 
        //and then you will know
        Dfs(s); //Dfs the root(s)
        steps(); //find the steps
        int a,b;
        while(m--)
        {
            a=read();b=read();
            printf("%d
    ",LCA(a,b));
        }
        return 0;
    }
    
    树上倍增英文版???
    1
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<stdio.h>
    #include<vector>
    #define maxn 500500
    using namespace std;
    ///隶属邻接表 
    struct Edge{                    //邻接表的结构体 
        int from,to;
    }edges[2*maxn];                 //边要乘2,因为是无向图 ; 
    int first[maxn],next[2*maxn];   //同理; 
    int read(){                        //读入优化,可以照着这个模板来写,这个还算写的比较好看。 
        int re=0;
        char ch=getchar();
        while (ch<'0' || ch>'9') ch=getchar();
        while (ch>='0' && ch<='9'){ 
            re=re*10+ch-'0'; 
            ch=getchar();
        }
        return re;
    }
    ///////////////////////////////////////////////
    ///全局变量 
    int n,m;
    int root;
    int height[maxn];
    float log2n;
    ///////////////////////////////////////////////////////
    ///隶属LCA的全局变量 
    int f[maxn][20];// 
    int have[maxn];                           //have,有没有找过,这都是套路 。 
    void dfs(int u,int h){                 //u代表点的标号,h代表高度。 
        int v;
        height[u]=h;
        for(int i=1;i<=log2n;i++) {
            if(h<=(1<<i)) break;              //由于i是从小到大计算的,故(1<<i)>=h 时可直接退出。请务必想清楚是<=  还是=。
            f[u][i] = f[ f[u][i-1] ][i-1]; //动规计算。同样也是一切倍增算法的核心。 
        }
        int k=first[u];
        while(k!=-1){
            v=edges[k].to;
            if(!have[v]) {
                have[v]=1;        
                f[v][0]=u;                 //将要找的下一个点的父节点标为当前处理的节点u。 
                dfs(v,h+1);
            }
            k=next[k];
        }
    }
    int require_LCA(int a,int b){
        int da=height[a],db=height[b];
    //第一步,将a,b两点移到同样的高度,只动高度大的那个点而不动高度小的那个点。 
        if(da!=db) {
            if(da<db){                   //保证a的高度是大于b的高度的。 
                swap(a,b);
                swap(da,db);
            }
            int d=da-db;
            for(int i=0;i<=log2n;i++) 
                if( (1<<i) & d) a=f[a][i]; //这里的位运算可以减少代码量
                                           //考虑到d是一个定值,而(1<<i)在二进制中只有第(i+1)位是1; 
                                           //那么d与(1<<i)如果某一位为1,那么表示可以向上移动, 
                                           //如果此时不移动,那么i增大了后就无法使height[a]==height[b]了 
        }
    //第二步,找到某个位置i,在这个位置时,f[a][i]!=f[b][i],但再向上移动一步,a,b相同了 
    //从log2n开始从大到小枚举i,如果超过了a,b的高度,则令i继续减小
    //如果没有超过a,b的高度,那么就判断移动了后会不会让a==b,
    //是,则i继续减小,否则,令此时的a=f[a][i],b=f[b][i]; 
        if(a==b) return b;
        int i=0;
        for(i=log2n;i>=0;i--) {
            if(height[ f[a][i] ]<0) continue;
            if( f[a][i]==f[b][i] ) continue;
            else a=f[a][i],b=f[b][i];        //顺便一提,在第二步任何地方没有break;
                                           //我就是因为在这里写了一个break,然后找了我两个小时啊。 
        }    
        return f[a][0];
    }
    /////////////////////////////////
    ///据说从主函数开始阅读是个好习惯。 
    int main(){
    //    freopen("in2.txt","r",stdin);
        n=read();m=read();root=read();
        memset(first,-1,sizeof(first));
        memset(next,-1,sizeof(next));
        int s,t;
        int dsd=2*(n-1);
        for(int i=1;i<=dsd;i+=2) {
            s=read();t=read();      //读入优化。 
            edges[i].from=s;
            edges[i].to=t;
            edges[i+1].from=t;
            edges[i+1].to=s;
            next[i]=first[s];
            first[s]=i;
            next[i+1]=first[t];
            first[t]=i+1;
        }
        // 以上是邻接表,在此不再赘述。 
        log2n=log(n)/log(2)+1;        //C++计算log是自然对数,我们要用的以2为底的对数,故要除以log(2); 
                                      //对无理数加上1或是0.5是个好习惯,可以减小误差; 
        memset(have,0,sizeof(have));
        memset(height,0,sizeof(height));
        memset(f,-1,sizeof(f));
        have[root]=1;                //fa[][]和height[]要在dfs理进行计算,不然根本找不到某个非根节点的父亲是谁; 
        dfs(root,1);                
        for(int i=1;i<=n;i++){
            for(int j=0;j<=log2n;j++) {
                if(height[i] <=(1<<j) ) break;
            }
        }
        for(int i=0;i<m;i++) {      //应对要求进行求解。 
            s=read();t=read();
            int y=require_LCA(s,t);
            printf("%d
    ",y);
        }
        return 0;
    }
    2

    End.

    如果运气好也是错,那我倒愿意错上加错!

    ❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

  • 相关阅读:
    new Date在不同浏览器识别问题
    22. Generate Parentheses dfs填表
    迪杰斯特拉+优先队列实现
    1062 最简分数 (20 分)
    1091 N-自守数 (15 分)
    1054 求平均值 (20 分)
    1045 快速排序 (25 分)
    1086 就不告诉你 (15 分)
    1076 Wifi密码 (15 分)
    1081 检查密码 (15 分)
  • 原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/6883274.html
Copyright © 2020-2023  润新知