首页 > 代码库 > 树上倍增 x

树上倍增 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 500500using 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