(倍增LCA求两点间的距离)Factory HDU - 6115

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)

1:这道题树上面的信息是字符串,而不是单个字母。
2:使用LCA来做的话,当n=1的时候,查询A B的时候答案应该是0,而不是1.
3:容器要清空!!!字符要初始化!!!
4:因为查询是从u到v,所以当u是祖先节点的时候,直接输出1就是了。
   当查询的两个点是同一个点以及n==1的时候,输出0。
   当v是祖先节点的时候,输出结果为abs(u的深度-v的深度)
   当u和v都不是祖先节点的时候,输出结果为abs(u的深度-祖先节点的深度)+1
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<string.h>
#include<string>
using namespace std;
const int maxn=1e5+10;
const int DEG=20;
int head[maxn],tot,top;
int fa[maxn][DEG],depth[maxn];
bool flag[maxn];
int father[maxn];
int n,m;
int number;
char x[50],y[50];
map<string,int>mpp;
struct Edge
{
    int to,next;
} edge[maxn*2];

struct xiao
{
    int u,w;
} mapp[maxn];

bool cmp(xiao a,xiao b)
{
    return a.u<b.u;
}

void addedge(int u,int v)
{
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

void debug()
{
    cout<<"____5_____"<<endl;
}

void init()
{
    mpp.clear();
    number=1;
    tot=0;
    memset(head,-1,sizeof(head));
    memset(flag,false,sizeof(flag));
    memset(depth,0,sizeof(depth));
    memset(fa,0,sizeof(fa));
}


void BFS(int root)
{
    queue<int>que;
    depth[root]=0;
    fa[root][0]=root;
    que.push(root);
    while(!que.empty())
    {
        int tmp=que.front();
        que.pop();
        for(int i=1; i<DEG; ++i)
        {
            fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
        }
        for(int i=head[tmp]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==fa[tmp][0])
                continue;
            depth[v]=depth[tmp]+1;
            fa[v][0]=tmp;
            que.push(v);
        }

    }
}

int LCA(int u,int v)
{

    if(depth[u]>depth[v])
    {
        int x=u;
        u=v;
        v=x;
    }
    int hu=depth[u],hv=depth[v];
    int tu=u,tv=v;
    for(int det=hv-hu,i=0; det; det>>=1,i++)
        if(det&1)
            tv=fa[tv][i];

    if(tu==tv)
        return tu;
    for(int i=DEG-1; i>=0; --i)
    {
        if(fa[tu][i]==fa[tv][i])
            continue;
        tu=fa[tu][i];
        tv=fa[tv][i];
    }

    return fa[tu][0];
}

int main()
{

    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<n;++i)
        {
            scanf("%s%s",x,y);
            int u,v;
            if(mpp[x]) //代表存在过了
                v=mpp[x];
            else
            {
                mpp[x]=number++;
                v=mpp[x];
            }
            if(mpp[y]) //代表存在过了
                u=mpp[y];
            else
            {
                mpp[y]=number++;
                u=mpp[y];
            }
            addedge(u,v);
            addedge(v,u);
            flag[v]=true;
        }
        int root=1;
        for(int i=1;i<=n;++i)
        {
            if(!flag[i])
            {
                root=i;
                break;
            }
        }
        BFS(root);
        while(m--)
        {
            scanf("%s%s",x,y);
            int u=mpp[x];
            int v=mpp[y];
            int ancester=LCA(u,v);
            int puts=0;
            if(u==v||n==1) puts=0;
            else if(ancester==u&&ancester!=v) puts=1;
            else if(ancester==v&&ancester!=u) puts=abs(depth[u]-depth[v]);
            else if(ancester!=v&&ancester!=u) puts=abs(depth[u]-depth[ancester])+1;
            cout<<puts<<endl;
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值