HDU 4547 CD操作【LCA_倍增】

http://acm.hdu.edu.cn/showproblem.php?pid=4547
CD操作
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 3020 Accepted Submission(s): 841

Problem Description

  在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录。
  这里我们简化一下问题,假设只有一个根目录,CD操作也只有两种方式:
  
  1. CD 当前目录名…\目标目录名 (中间可以包含若干目录,保证目标目录通过绝对路径可达)
  2. CD .. (返回当前目录的上级目录)
  
  现在给出当前目录和一个目标目录,请问最少需要几次CD操作才能将当前目录变成目标目录?

Input

输入数据第一行包含一个整数T(T<=20),表示样例个数;
每个样例首先一行是两个整数N和M(1<=N,M<=100000),表示有N个目录和M个询问;
接下来N-1行每行两个目录名A B(目录名是只含有数字或字母,长度小于40的字符串),表示A的父目录是B。
最后M行每行两个目录名A B,表示询问将当前目录从A变成B最少要多少次CD操作。
数据保证合法,一定存在一个根目录,每个目录都能从根目录访问到。

Output

请输出每次询问的结果,每个查询的输出占一行。

Sample Input

2
3 1
B A
C A
B C

3 2
B A
C B
A C
C A

Sample Output

2
1
2

Source

2013金山西山居创意游戏程序挑战赛——初赛(1)

Recommend

liuyiding | We have carefully selected several similar problems for you: 6297 6296 6295 6294 6293

LCA_倍增

#include<cstring>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<iostream>
#include<string>
#include<map>
using namespace std;
const int maxn = 1e5 + 10;
map<string, int> mp;
int n, m, dep[maxn], fa[maxn][20], rt, head[maxn], sz = 0, Log[maxn];
bool f[maxn];
struct edge {
    int v, w, nx;
}e[maxn];
string s1, s2;
inline void add(int u, int v) {
    e[++sz].v = v; e[sz].nx = head[u]; head[u] = sz;
}
void init();
void dfs(int x) {
    for (int i = 1; i <= Log[n]; i++) {
        if (!fa[x][i - 1]) break;
        fa[x][i] = fa[fa[x][i-1]][i - 1];
    }
    for (int i = head[x]; i != -1; i = e[i].nx) {
        int v = e[i].v; dep[v] = dep[x] + 1; dfs(v);
    }
}
int lca(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    int d = dep[u] - dep[v];
    for (int i = 0; i <= Log[d]; i++) {
        if (d >> i & 1) u = fa[u][i];
    }
    if (u == v) return u;
    for (int i = Log[n]; i >= 0; i--) {
        if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
    }
    return fa[u][0];
}

int main() {
    int T;
    Log[0] = -1;
    for (int i = 1; i <= maxn - 10; i++) Log[i] = Log[i >> 1] + 1;
    scanf("%d", &T); bool flag = 0;
    while (T--) {
        //if (flag) printf("\n");
        flag = 1;
        scanf("%d %d", &n, &m);
        init(); int tot = 0;
        for (int i = 1; i < n; i++) {
            cin >> s1 >> s2;
            if (!mp[s1]) mp[s1] = ++tot;
            if (!mp[s2]) mp[s2] = ++tot;
            fa[mp[s1]][0] = mp[s2]; f[mp[s1]] = 1; add(mp[s2], mp[s1]);
        }
        for (int i = 1; i <= n; i++) if (!f[i]) rt = i;
        dep[rt] = 0; dfs(rt);
        while (m--) {
            cin >> s1 >> s2;
            int u = mp[s1], v = mp[s2], t = lca(u, v);
            if (u == v) puts("0");
            else if (u == t) puts("1");
            else if (v == t) printf("%d\n", dep[u] - dep[v]);
            else printf("%d\n", dep[u] - dep[t] + 1);
        }
    }
    return 0;
}
void init()
{
    sz = 0;
    memset(f, 0, sizeof f);
    memset(fa, 0, sizeof fa);
    memset(head, -1, sizeof head);
    mp.clear();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值