FZU 2207 ——以撒的结合——————【LCA + 记录祖先】

Problem 2207 以撒的结合

Accept: 47    Submit: 161
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 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

 Source

FOJ有奖月赛-2015年11月
 
 
解题思路:首先我们求出u,v两点的LCA为x,然后来判断出k在u-x段还是在v-x段。然后记录每个点u与祖先ancestor的距离,然后O(1)查询。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<math.h>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long LL;
#define mid (L+R)/2
#define lson rt*2,L,mid
#define rson rt*2+1,mid+1,R
const int maxn = 1300;
const LL mod = 1000000007;
const int INF = 0x3f3f3f3f;
struct AdjEdge{
    int to,w,next;
}adjedges[maxn*2];
int head[maxn];
int vset[maxn*2],dep[maxn],d[maxn*2][30],first[maxn];
int fa[maxn][maxn];
int tot,nn;
void init(){
    tot=0;
    nn=0;
    memset(dep,0,sizeof(dep));
    memset(head,-1,sizeof(head));
    memset(d,0,sizeof(d));
    memset(first,0,sizeof(first));
}
void addedge(int _u,int _v){     //
    adjedges[tot].to=_v;
    adjedges[tot].next=head[_u];
    head[_u]=tot++;
//    adjedges[tot].to=_u;
//    adjedges[tot].next=head[_v];
//    head[_v]=tot++;
}

void dfs(int _u,int _fa,int _dep){
   // printf("%d %d
",_u,_dep);
    dep[_u]=_dep;
    vset[++nn]=_u;
    first[_u]=nn;
    fa[_u][0] = _u;
    for(int i = 1; i <= _dep; i++){
        fa[_u][i] = fa[_fa][i-1];
    }
    for(int i=head[_u];i!=-1;i=adjedges[i].next){
        AdjEdge & e = adjedges[i];
        if(e.to!=_fa){
            dfs(e.to,_u,_dep+1);
            vset[++nn]=_u;
        }
    }
}
void ST(){
    for(int i=1;i<=nn;i++)
        d[i][0]=vset[i];
    for(int j=1; (1<<j)<=nn; j++){
        for(int i=1; i+(1<<j)-1<=nn; i++){
            if(dep[d[i][j-1]]<dep[d[i+(1<<(j-1))][j-1]])
                d[i][j]=d[i][j-1];
            else d[i][j]=d[i+(1 << (j-1))][j-1];
        }
    }
}

int RMQ(int L,int R){
    int k=0;
    while((1<<(k+1))<=R-L+1) k++;
    if(dep[d[L][k]]<=dep[d[R-(1<<k)+1][k]])
        return d[L][k];
    return d[R-(1<<k)+1][k];
}
void tes(){
    for(int i =1; i <= 7; i++){
        for(int j = 0; j <= 4; j++){
            printf("%d ",fa[i][j]);
        }puts("");
    }
}
int main(){
    int T, n, q, cas = 0;
    scanf("%d",&T);
    while(T--){
        init();
        int a,b,k;
        scanf("%d%d",&n,&q);
        for(int i=1; i<n; i++){
            scanf("%d%d",&a,&b);
            addedge(a,b);
            addedge(b,a);
        }
        memset(fa,0,sizeof(fa));
        dfs(1,0,1);    //dep 1
        ST();
     //   tes();
        printf("Case #%d:
",++cas);
        int x;
        for(int i=0; i<q; i++){
            scanf("%d%d%d",&a,&b,&k);
            if(first[a]<=first[b]){
                x = RMQ(first[a],first[b]);
            }else{
                x = RMQ(first[b],first[a]);
            }
            if(k == 1){
                printf("%d
",a);
            }else if(dep[a] + dep[b] - 2*dep[x] + 1 == k){
                printf("%d
",b);
            }else if(dep[a] - dep[x] + 1 >= k){
                printf("%d
",fa[a][k-1]);
            }else{
                int kk = dep[a] + dep[b] - 2*dep[x] - k + 1;
                printf("%d
",fa[b][kk]);
            }
        }
    }
    return 0;
}

/*
55
7 10
1 2
1 3
2 4
2 5
5 6
5 7

4 3 3
4 3 2
7 4 3
4 7 3
4 7 4

*/

  

 
 
原文地址:https://www.cnblogs.com/chengsheng/p/5288003.html