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\n",_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:\n",++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\n",a); }else if(dep[a] + dep[b] - 2*dep[x] + 1 == k){ printf("%d\n",b); }else if(dep[a] - dep[x] + 1 >= k){ printf("%d\n",fa[a][k-1]); }else{ int kk = dep[a] + dep[b] - 2*dep[x] - k + 1; printf("%d\n",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 */
学学学 练练练 刷刷刷