树剖 lca

GeneralLiu

    橙边为轻边

  红边为重边

  绿数为每个点的 top

  橙数为每个点的编号

 

步骤

1  先预处理 每个点的 deep深度  size子树大小  dad父节点  

2  再预处理 每个点的 top重链顶点

3  就是跳了

 

应用

 

洛谷 P2912 [USACO08OCT] 牧场散步

效率蛮高的

 

此题中用 len[i] 表示 i 到根距离

询问 x 和 y 的距离

答案可用 len[x] + len[y] - 2*len[ lca(x,y) ]表示

而 lca 可以用树剖求出

len[] 在树剖求lca 的预处理中 可以顺带求出来

 

树剖求 lca 虽然不常用

但它确实很吊

嘻嘻 我的 树剖代码 目前是本题 的 rank1

时间短 空间小

 

#include<bits/stdc++.h>
#define N 1003
using namespace std;
int cnt,head[N],to[N<<1],dis[N<<1],next[N<<1],len[N],size[N],deep[N],dad[N],n,m,top[N];
int read(){//读入优化 
    char ch=getchar();
    int ans=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){
        ans=ans*10+ch-'0';
        ch=getchar();
    }
    return ans;
}
int lca(int x,int y){//树剖求lca
    for(;top[x]!=top[y];){
        if(deep[top[x]]<deep[top[y]])x^=y^=x^=y;//利用二进制的 swap() 
        x=dad[top[x]];
    }
    if(deep[x]>deep[y])x^=y^=x^=y;
    return x;
}
void dfs(int k){//步骤1
    int v;
    size[k]=1,deep[k]=deep[dad[k]]+1;
    for(int i=head[k];i;i=next[i]){
        v=to[i];
        if(v!=dad[k])//各种预处理,包括针对本题的 len[]数组 
            len[v]=dis[i]+len[k],dad[v]=k,dfs(v),size[k]+=size[v];
    }
}
      
void dfs1(int k){//步骤2
    int t=0,v;
    if(!top[k])top[k]=k;
    for(int i=head[k];i;i=next[i]){
        v=to[i];
        if(v!=dad[k]&&size[t]<size[v])
            t=v;
    }
    if(t)top[t]=top[k],dfs1(t);
    for(int i=head[k];i;i=next[i]){
        v=to[i];
        if(v!=dad[k]&&v!=t)
            dfs1(v);
    }   
}
int main(){
    n=read(),m=read();
    int x,y,z;
    while(--n){
        x=read(),y=read(),z=read();
        next[++cnt]=head[x];
        to[cnt]=y;
        head[x]=cnt;
        dis[cnt]=z;
        next[++cnt]=head[y];
        to[cnt]=x;
        head[y]=cnt;
        dis[cnt]=z;
    }
    dfs(1);
    dfs1(1);
    while(m--){
        x=read(),y=read();
        printf("%d\n",len[x]+len[y]-(len[lca(x,y)]<<1));
    }
    return 0;
}

 

posted @ 2017-05-05 15:40  GeneralLiu  阅读(1383)  评论(0编辑  收藏  举报