POJ1470 倍增法在线求LCA

其实本质是就是一种二分。

首先我们先对这个树DFS一遍,记录每个节点的深度(depth),并且记录他的第2^j个祖先。

当我们找u和v的LCA时,首先把他们的深度变成一样的,然后如果pre[u][k] == pre[v][k],那么当前是公共祖先,但不一定是最近的,所以k--;

如果不符合就u = pre[u][k] v = pre[v][k],接着找。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

struct node{
    int v,next;
}edge[111111];
int pre[999][111];
int head[1111];
int depth[1111];
int ans[1111];
int n;
int e;
int root[1111];
void addedge(int u,int v){
    edge[e].v = v;
    edge[e].next = head[u];
    head[u] = e++;
}

void dfs(int k){
    for(int i = head[k] ; i != -1 ; i = edge[i].next){
        int v = edge[i].v;
        pre[v][0] = k;
        int m = k;
        depth[v] = depth[k] + 1;
        for(int j = 0 ; pre[m][j] != 0 ; j ++){
            pre[v][j+1] = pre[m][j];
            m = pre[m][j];
        }
        dfs(v);
    }
}

int LCA(int u,int v){
    if(u == v)
        return u;
    if(depth[u] < depth[v]){
        swap(u,v);
    }
    int m = depth[u] - depth[v];
    int k = 0;
    while(m){
        if(m%2 == 1){
            u = pre[u][k];
        }
        m /= 2;
        k++;
    }
    if(u == v){
        return u;
    }
    k = 0;
    while(u != v){
        if(pre[u][k] != pre[v][k] || (pre[u][k] == pre[v][k] && k == 0)){
            u = pre[u][k];
            v = pre[v][k];
            k++;
        }
        else{
            k--;
        }
    }
    return u;
}

void init(){
    int e = 0;
    memset(head,-1,sizeof(head));
    memset(depth,0,sizeof(depth));
    memset(pre,0,sizeof(pre));
    memset(ans,0,sizeof(ans));
    for(int i = 1 ; i <= n ; i ++){
        root[i] = i;
    }
}
int main()
{
    while(scanf("%d",&n) != EOF){
        init();
        for(int i = 1 ; i <= n ; i ++){
            int id,num;
            scanf("%d:",&id);
            scanf("(%d)",&num);
            for(int j = 0 ; j < num ; j ++){
                int v;
                scanf("%d",&v);
                root[v] = id;
                addedge(id,v);
            }
        }
        int r = 1;
        while(root[r] != r){
            r = root[r];
        }
        dfs(r);
        int q;
        scanf("%d",&q);
        while(q--){
            char c1[2],c2[2];
            int u,v;
            scanf("%1s%d%d%1s",c1,&u,&v,c2);
            ans[LCA(u,v)]++;
        }
        for(int i = 1 ; i <= n ; i ++){
            if(ans[i] != 0){
                printf("%d:%d\n",i,ans[i]);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值