FZU 2207 以撒的结合(LCA+记录路径)

Problem Description

小茗同学最近在认真地准备比赛,所以经常玩以撒的结合。

《以撒的结合》是一款由Edmund McMillen,Florian Himsl 开发,并由Edmund McMillen最早于2011年09月29日发行的一款2D平面角色扮演、动作冒险类的独立游戏。游戏的角色将在有着能够提升能力的道具与特殊技能的半RPG世界中闯荡。

——来自百度百科

小茗同学在打BOSS前,费掉了很多HP。在地图的一些房间里有补充HP的红心,然而小茗同学受到了看不见地图的诅咒。凭借不知道哪里来的记忆,小茗同学记得某个有红心的房间在房间A与房间B的路上的第K个房间里。为了简化问题,我们把地图看成一棵树。小茗同学想知道A到B的第K个房间号为多少,由于小茗同学很累,所以现在这个任务交给你了。

Input

第一行是一个整数T(T<=10),表示有T组测试数据。

每组数据的第一行为两个整数n m(0 < n< 1000,0< m <= n*n),分别表示房间个数和询问次数。

接下来n-1行,每行两个整数u v(0 < u、v<=n,且u≠v),表示地图上房间u和房间v有一条路径。

最后是m行,每行三个整数u v k,表示询问房间u到房间v的路径上的第k个房间。

输入数据保证合法,即k不超过u、v的最短距离。

Output

对于每组数据,首先第一行先输出“Case #x:“ ,其中x是从1开始,表示数据组号,接下来m行,每行输出相应的房间号。

Sample Input

1
6 3
1 2
2 4
2 5
1 3
3 6
4 6 4
1 6 2
4 5 3
Sample Output

Case #1:
3
3
5
题解:
先求出lca后,根据大小关系判断k在哪边就好。记录路径更便于输出。
代码:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
const int N=1e3+10;
int cnt,u,v,k,tot,n,q,t;
int dep[N],fa[N][N],siz[N],son[N];
struct node{int to,next;}edge[N<<2];
int head[N<<2];
void add(int u,int v)
{
    edge[cnt].to=v,edge[cnt].next=head[u],head[u]=cnt++;
    edge[cnt].to=u,edge[cnt].next=head[v],head[v]=cnt++;
}
void dfs1(int u,int f,int d)
{
    fa[u][0]=u;
    for(int i=1;i<=d;i++)
    {
        fa[u][i]=fa[f][i-1];
    }
    dep[u]=d,siz[u]=1,son[u]=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==f)continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v]) son[u]=v;
    }
}
int top[N],pre[N],tree[N];
void dfs2(int u,int tp)
{
    top[u]=tp,tree[u]=++tot,pre[tree[u]]=u;
    if(!son[u]) return ;
    dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[u][1]||v==son[u]) continue;
        dfs2(v,v);
    }

}
void init()
{
    tot=0;
    cnt=0;
    memset(head,-1,sizeof(head));
    memset(fa,0,sizeof(fa));
}
int lca(int x,int y)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
        x=fa[fx][1],fx=top[x];
    }
    if(dep[x]>dep[y]) swap(x,y);
    return x;
}
int main()
{
    scanf("%d",&t);
   for(int o=1;o<=t;o++)
   {
        printf("Case #%d:\n",o);
        init();
        scanf("%d%d",&n,&q);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
        }
         dfs1(1,0,1),dfs2(1,1);
        while(q--)
        {
          scanf("%d%d%d",&u,&v,&k);
          int lc=lca(u,v);
          if(k==1)
          {
              printf("%d\n",u);

          }
          else if(dep[u]+dep[v]-2*dep[lc]+1==k)
          {
              printf("%d\n",v);
          }
          else if(dep[u]-dep[lc]+1>=k)
          {
              printf("%d\n",fa[u][k-1]);
          }
          else
         {
             int kk=dep[u]+dep[v]-2*dep[lc]-k+1;
             printf("%d\n",fa[v][kk]);
         }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值