HDU 4547 CD操作 2013金山西山居创意游戏程序挑战赛——初赛(1)

CD操作

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 458    Accepted Submission(s): 126


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
这个题目是经典的LCA算法,比赛的时候看完题就想到了,但是当时不会LCA。。。所以这两天发奋学习,学会了离线算法。

具体思路就是找到查询的公共祖先。如果他们祖先是起点,那么直接一次CD,如果公共祖先是终点那么结果是高度差,如果起点和重点一样,那么为0,其他情况是先用起点和公共祖先的高度差步数走到最进公共祖先,然后再一次CD达到目的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
map<string,int> mp;
int n,m,idx,root,ne,ne1;
int pre[100010];
int deep[100010];
int income[100010];
int head[100010];
int head1[200010];
struct mnode
{
    int u,v,next;
}edge[100010];
struct qnode
{
    int u,v,Ans,next;
}edge1[200010];
void add(int u,int v)
{
    edge[ne].u=u,edge[ne].v=v,edge[ne].next=head[u],head[u]=ne++;
}
void add1(int u,int v)
{
    edge1[ne1].u=u,edge1[ne1].v=v,edge1[ne1].next=head1[u],head1[u]=ne1++;
    edge1[ne1].u=v,edge1[ne1].v=u,edge1[ne1].next=head1[v],head1[v]=ne1++;
}
int findx(int x)
{
    return pre[x]=(pre[x]==x?x:findx(pre[x]));
}
void Unicon(int u,int v)
{
    pre[findx(v)]=findx(u);
}
void init()
{
    idx=1;
    ne=0;
    ne1=0;
    mp.clear();
    for (int i=1;i<=n;i++)
    {
        head[i]=-1;head1[i]=-1;
        pre[i]=-1;
        income[i]=0;
    }
}
void dfs(int u,int dep)
{
    int i,v;
    deep[u]=dep;
    pre[u]=u;
    for (i=head[u];i!=-1;i=edge[i].next)
    {
        v=edge[i].v;
        if (pre[v]==-1)
        {
            dfs(v,dep+1);
            Unicon(u,v);
        }
    }
    for (i=head1[u];i!=-1;i=edge1[i].next)
    {
        v=edge1[i].v;
        if (pre[v]!=-1) edge1[i].Ans=edge1[i^1].Ans=findx(v);
    }

}
int main()
{
    int t,i,ss,tt;
    char name1[50],name2[50];
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        for (i=1;i<n;i++)
        {
            scanf("%s%s",name1,name2);
            if (!mp[name1]) mp[name1]=idx++;
            if (!mp[name2]) mp[name2]=idx++;
            income[mp[name1]]++;
            add(mp[name2],mp[name1]);
        }
        for (i=1;i<idx;i++) if (income[i]==0) {root=i;break;}
        for (i=1;i<=m;i++)
        {
            scanf("%s%s",name1,name2);
            ss=mp[name1],tt=mp[name2];
            add1(ss,tt);
        }
        dfs(root,0);
        for (i=0;i<ne1;i+=2)
        {

            if (edge1[i].u==edge1[i].v) puts("0");
            else if (edge1[i].Ans==edge1[i].u) puts("1");
            else if (edge1[i].Ans==edge1[i].v)
                printf("%d\n",deep[edge1[i].u]-deep[edge1[i].v]);
            else
                printf("%d\n",deep[edge1[i].u]-deep[edge1[i].Ans]+1);
        }
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值