HDU 4547 倍增LCA

本文介绍了一种解决CD操作问题的算法,该问题旨在找到从一个目录切换到另一个目录所需的最少CD命令次数。通过构建目录间的父子关系,并利用广度优先搜索和最低公共祖先(LCA)算法来高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CD操作

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


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
 


题意较裸,以前太弱,不会,最近做一下。

代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<string.h>
#include<string>
#include<map>
#include<string>
#include<queue>
#include<set>
using namespace std;
const int maxn=111111;
const int DEG=22;
struct node
{
         int next,to;
}edge[maxn*2];
int head[maxn],tol,m,n;
void add(int u,int v)
{
         edge[tol].to=v;
         edge[tol].next=head[u];
         head[u]=tol++;
}
void init()
{
         memset(head,-1,sizeof(head));
         tol=0;
}
int fa[maxn][DEG];
int deg[maxn];
void bfs(int root)
{
         queue<int> q;
         deg[root]=0;
         fa[root][0]=root;
         q.push(root);
         while(!q.empty())
         {
                  int u=q.front();q.pop();
                  for(int i=1;i<DEG;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
                  for(int i=head[u];i!=-1;i=edge[i].next)
                  {
                           int v=edge[i].to;
                           if(v==fa[u][0])continue;
                           deg[v]=deg[u]+1;
                           fa[v][0]=u;
                           q.push(v);
                  }
         }
}
int lca(int u,int v)
{
         if(deg[u]>deg[v])swap(u,v);
         int hu=deg[u],hv=deg[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];
}
bool flag[maxn];
int cnt;
map<string,int> mp;
int find(char *str)
{
         if(mp.find(str)!=mp.end())return mp[str];
         mp[str]=++cnt;
         return cnt;
}
int main()
{
         int i,j,k,T;
         scanf("%d",&T);
         while(T--)
         {
                  scanf("%d%d",&n,&m);
                  init();
                  memset(flag,0,sizeof(flag));
                  cnt=0;
                  mp.clear();
                  char str1[1000],str2[1000];
                  for( i=1;i<n;i++)
                  {
                           scanf("%s%s",str1,str2);
                           j=find(str1);
                           k=find(str2);
                           add(k,j);
                           flag[j]=1;
                  }
                  int root;for( i=1;i<=n;i++)if(flag[i]==0)
                  {
                           root=i;
                           break;
                  }
                  bfs(root);
                  while(m--)
                  {
                          scanf("%s%s",str1,str2);
                          i=find(str1);
                          j=find(str2);
                          k=lca(j,i);
                          int ans=0;
                          ans+=deg[i]-deg[k];
                          if(deg[k]!=deg[j])ans++;
                          printf("%d\n",ans);
                  }
         }
         return 0;
}


/*

7 7
2 1
4 2
5 2
3 1
6 3
7 3












*/


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值