L2-030 冰岛人 LCA 暴力

题解

首先说明一下题意,有些题面没说明白。如果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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值