题解
首先说明一下题意,有些题面没说明白。如果A是C的第5代,B是C的第4代则不满足,要求最近公共祖先是两个人的5代及以上。
如果查询的是起源人也是输出NA。给出的名称最后表示性别的m和f不算为名称后缀,查询时会带有。
题目实际上就是给定一个图,问两个点是否有5代以内的最近公共祖先。由于给的是字符串,需要先用map编号为1~n的点处理。
映射编号建图后,从祖先节点0开始DFS求出每个点的深度。找LCA时深度深的先向上跳,只有当深度相同时k才减少。
由于只跳5次数据量不大直接暴力跳LCA就行。接受询问时,姓氏可以直接丢弃掉。
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int f[N], s[N], d[N]; //父节点 性别 节点深度
char buf[100];
string a[N], b[N];
vector<int> e[N]; //父节点到儿子的边
void DFS(int x, int dep) //求节点深度
{
d[x] = dep;
for (int y : e[x])
DFS(y, dep + 1);
}
bool LCA(int x, int y, int k)
{
if (!k || !x || !y) //最近的次数用尽或到达祖宗
return 0;
if (x == y)
return 1;
if (d[x] == d[y])
return LCA(f[x], f[y], k - 1); //最近的一个至少5代
if (d[x] > d[y]) //深的先跳
return LCA(f[x], y, k);
return LCA(x, f[y], k);
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
for (int i = 1; i < N; ++i)
f[i] = i; //自己是自己的父节点
map<string, int> mp;
int idx = 0;
int n, m;
cin >> n;
for (int i = 0; i < n; ++i)
{
scanf("%s", buf);
a[i] = buf;
scanf("%s", buf);
b[i] = buf;
mp[a[i]] = ++idx; //给自己标号
s[idx] = b[i].back() == 'n' || b[i].back() == 'm';
if (b[i].back() == 'r') //消除名称后缀 不消除mf
b[i].resize(b[i].size() - 7);
else if (b[i].back() == 'n')
b[i].resize(b[i].size() - 4);
}
for (int i = 0; i < n; ++i)
{
int x = mp[a[i]], y = mp[b[i]];
f[x] = y, e[y].push_back(x); //建立关系 祖宗节点为0
}
DFS(0, 0);
cin >> m;
for (int i = 0; i < m; ++i)
{
string x, y;
scanf("%s", buf);
x = buf;
scanf("%s", buf); //丢弃姓氏
scanf("%s", buf);
y = buf;
scanf("%s", buf);
int a = mp[x], b = mp[y];
if (!a || !b) //祖宗也算NA
printf("NA\n");
else if (s[a] == s[b])
printf("Whatever\n");
else
printf("%s\n", LCA(a, b, 4) ? "No" : "Yes");
}
return 0;
}