树上倍增 x

Posted 云深不知处

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树上倍增 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\n",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\n",y);
    }
    return 0;
}
2

 

 

 

End.

以上是关于树上倍增 x的主要内容,如果未能解决你的问题,请参考以下文章

两种lca的求法:树上倍增,tarjan

NKOJ P3815 树上的询问 (LCA 倍增)(复习距离倍增)

洛谷 P2680 [NOIP2015 提高组] 运输计划(二分,树上查分,树上倍增,LCA)

树上倍增求LCA详解

MST(最小生成树+倍增)

BZOJ5165: 树上倍增