hdu 2586 最近公共祖先

题目链接

题意


n个村子,被n-1个条边连接,每次询问u和v的最近距离。


解析


  1. 求u和v的到他们的最近公共祖先的距离即可。
  2. 基本算法
    1. 在线算法 倍增法
      每次询问O(logN)
      d[i] 表示 i节点的深度, p[j,i] 表示 i 的 2^j 倍祖先
      那么就有一个递推式子 p[j,i]=p[p[j,i-1],i-1]
      这样子一个O(NlogN)的预处理求出每个节点的 2^k 的祖先
      然后对于每一个询问的点对a, b的最近公共祖先就是:
      先判断是否 d[a] > d[b] ,如果是的话就交换一下(保证 a 的深度小于 b 方便下面的操作)然后把b 调到与a 同深度, 同深度以后再把a, b 同时往上调(dec(j)) 调到有一个最小的j 满足p[j,a]!=p[j,b] (a b 是在不断更新的), 最后再把 a, b 往上调 (a=p[0,a], b=p[0,b]) 一个一个向上调直到a = b, 这时 a or b 就是他们的最近公共祖先

相关知识

http://baike.baidu.com/link?url=CxXG3rjGTlknKSJg17LrHjD5EVnoIrG_9GtS5ejbRp1GPv0FiGC-rB53pEcrGYkTnWUVQyvCCOu23KIVTDl_W_

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn = 40000+100;
typedef long long LL;
int parent[20][maxn], dis[maxn];
int dep[maxn];

vector<pair<int, int> >g[maxn];

void dfs(int u, int p, int d, int D) {
    parent[0][u] = p;
    dep[u] = d;
    for (int i=0; i<g[u].size(); i++) {
        if (g[u][i].first != p)
            dfs(g[u][i].first, u, d+1, D+g[u][i].second);
    }
    dis[u] = D;
}

void init(int V) {
    dfs(0, -1, 0, 0);
    for (int k=0; k+1 < 20; k++) {
        for (int v=0; v<V; v++) {
            if (parent[k][v] < 0)
                parent[k+1][v] = -1;
            else
                parent[k+1][v] = parent[k][parent[k][v]];
        }
    }
}

int lca(int u, int v) {
    if (dep[u] > dep[v])
        swap(u, v);
    for (int i=0; i<20; i++)
        if ((dep[v]-dep[u]) >> i & 1)
            v = parent[i][v];
    if (u == v)
        return u;
    for (int j=20-1; j>=0; j--) {
        if (parent[j][u] != parent[j][v]) {
            u = parent[j][u];
            v = parent[j][v];
        }
    }
    return parent[0][u];
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, m;
        memset(dis, 0, sizeof(dis));
        scanf("%d%d", &n, &m);
        for (int i=0; i<n-1; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            g[u-1].push_back(make_pair(v-1, w));
            g[v-1].push_back(make_pair(u-1, w));
        }
        init(n);
        for (int i=0; i<m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            int p = lca(u-1, v-1);
            printf("%d\n", dis[u-1]+dis[v-1]-2*dis[p]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值