2018年世界杯,冰岛队因1:1平了强大的阿根廷队而一战成名。好事者发现冰岛人的名字后面似乎都有个“松”(son),于是有网友科普如下:
冰岛人沿用的是维京人古老的父系姓制,孩子的姓等于父亲的名加后缀,如果是儿子就加 sson,女儿则加 sdottir。因为冰岛人口较少,为避免近亲繁衍,本地人交往前先用个 App 查一下两人祖宗若干代有无联系。本题就请你实现这个 App 的功能。
输入格式:
输入首先在第一行给出一个正整数 N(1<N≤105),为当地人口数。随后 N 行,每行给出一个人名,格式为:名 姓(带性别后缀)
,两个字符串均由不超过 20 个小写的英文字母组成。维京人后裔是可以通过姓的后缀判断其性别的,其他人则是在姓的后面加 m
表示男性、f
表示女性。题目保证给出的每个维京家族的起源人都是男性。
随后一行给出正整数 M,为查询数量。随后 M 行,每行给出一对人名,格式为:名1 姓1 名2 姓2
。注意:这里的姓
是不带后缀的。四个字符串均由不超过 20 个小写的英文字母组成。
题目保证不存在两个人是同名的。
输出格式:
对每一个查询,根据结果在一行内显示以下信息:
- 若两人为异性,且五代以内无公共祖先,则输出
Yes
; - 若两人为异性,但五代以内(不包括第五代)有公共祖先,则输出
No
; - 若两人为同性,则输出
Whatever
; - 若有一人不在名单内,则输出
NA
。
所谓“五代以内无公共祖先”是指两人的公共祖先(如果存在的话)必须比任何一方的曾祖父辈分高。
输入样例:
15
chris smithm
adam smithm
bob adamsson
jack chrissson
bill chrissson
mike jacksson
steve billsson
tim mikesson
april mikesdottir
eric stevesson
tracy timsdottir
james ericsson
patrick jacksson
robin patricksson
will robinsson
6
tracy tim james eric
will robin tracy tim
april mike steve bill
bob adam eric steve
tracy tim tracy tim
x man april mikes
输出样例:
Yes
No
No
Whatever
Whatever
NA
做法:
先讲一下测试点:
测试点3和6所谓“五代以内无公共祖先”是指两人的公共祖先(如果存在的话)必须比任何一方的曾祖父辈分高。比如说两个人有一个公共祖先,分别作为一方的6代,一方的4代,则这两个人不能成婚。
测试点4 是关于NA的,每个人的父亲不一定出现在表中
测试点2 只需记录维京家族的父辈,每个维京家族的起源人都是男性
接下来说做法:
1.存储所有人的性别和维京人的父亲
2.输入一对人名
2.1根据sex值是否为0,判断其是否出现在名单中
2.2判断是否同性
2.3判断这对异性是否可以成婚
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
using namespace std;
const int N = 200010;
unordered_map<string,string> fa;//记录维京人的父亲
unordered_map<string,int> sex;//1男 2女
unordered_map<string,int> st;//判断有无共同祖先用
bool check(string m1,string m2)
{
st.clear();//清空
int j = 1;
for(string i = m1;i.size();i = fa[i],j++)//标记
st[i] = j;
j = 1;
for(string i = m2;i.size();i = fa[i],j++)//判断
{
//cout << i << " " << st[i] << " " << j << endl;
if(st[i]) //有相同祖先
{
if(st[i] >= 5 && j >= 5) return true;
else return false;
}
st[i] = j;
}
return true;
}
int main()
{
int n = 0;
cin >> n;
while(n--)
{
string x,m;
cin >> m >> x;
//记录所有人的性别
if(x.back() == 'm') sex[m] = 1;
else if(x.back() == 'f') sex[m] = 2;
else if(x.back() == 'n')//记录维京人的父亲
{
sex[m] = 1;
fa[m] = x.substr(0,x.size() - 4);
}
else
{
sex[m] = 2;
fa[m] = x.substr(0,x.size() - 7);
}
}
cin >> n;
while(n--)
{
string x1,m1,x2,m2;
cin >> m1 >> x1 >> m2 >> x2;
//在哈希表中插入一个键,若不赋值,数值型默认为0,字符串则为空串
if(!sex[m1] || !sex[m2]) puts("NA");//因为我们记录了所有人的性别,所以可以通过查询性别判断是否在名单中
else if(sex[m1] == sex[m2]) puts("Whatever");
else if(check(m1,m2)) puts("Yes");
else puts("No");
}
return 0;
}
//测试点3和6所谓“五代以内无公共祖先”是指两人的公共祖先(如果存在的话)必须比任何一方的曾祖父辈分高。3 6 不行
//测试点4 是关于NA的
//测试点2 只需记录维京家族的父辈,每个维京家族的起源人都是男性
二刷代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
using namespace std;
const int N = 100010;
unordered_map<string,int> ha;
int f[N],sex[N],st[N],idx;
int check(int p1,int p2)
{
memset(st,0,sizeof st);
for(int i = p1,j = 1;i;i = f[i],j++)
st[i] = j;
for(int i = p2,j = 1;i;i = f[i],j++)
if(st[i])//第一个公共祖先
{
if(st[i] >= 5 && j >= 5) return 1;
else return 0;
}
return 1;
}
void test(string name)
{
int t = ha[name];
cout << name << " " << t << " " << sex[t] << endl;
}
int main()
{
int n = 0;
cin >> n;
for(int i = 0;i < n;i++)
{
string m,x;
cin >> m >> x;
if(!ha[m]) ha[m] = ++idx;
int t = ha[m];
if(x.back() == 'm') sex[t] = 1;
else if(x.back() == 'f') sex[t] = 2;
else if(x.back() == 'n')
{
sex[t] = 1;
string fm = x.substr(0,x.size() - 4);
if(!ha[fm]) ha[fm] = ++idx;
f[t] = ha[fm];
}
else
{
sex[t] = 2;
string fm = x.substr(0,x.size() - 7);
if(!ha[fm]) ha[fm] = ++idx;
f[t] = ha[fm];
}
//test(m);
}
int m = 0;
cin >> m;
while(m--)
{
string m1,x1,m2,x2;
cin >> m1 >> x1 >> m2 >> x2;
//test(m1);test(m2);
if(!sex[ha[m1]] || !sex[ha[m2]]) puts("NA");
else if(sex[ha[m1]] == sex[ha[m2]]) puts("Whatever");
else
{
if(check(ha[m1],ha[m2])) puts("Yes");
else puts("No");
}
}
return 0;
}