2023-7-14

题目

luogu题目传送门

题目描述

Nina 和 Emilija 正在玩一个特殊的游戏。这个游戏是在一张最开始为空白的纸上进行的。在每一个人的行动回合内,这个人会在这张纸上当前的单词后面加入一个字母。她们会轮流行动,而 Nina 先手行动。

操作者必须保证这样一个条件:在添加完一个字符后,整张纸上的单词必须是操作人最喜欢的歌曲的一个单词的前缀。如果不满足条件,进行这个操作的人就输了。

你的问题是,如果两个人都采取最优策略,那么谁会获得最后的胜利。

输入格式

第一行包含一个正整数 \(n\),表示 Nina 最喜爱歌曲中的单词数量。

接下来的 \(n\) 行,每一行输入 Nina 最喜爱歌曲中的一个单词。

接下来的一行包含一个正整数 \(m\),表示 Emilija 最喜爱歌曲中的单词数量。

接下来的 \(m\) 行,每一行输入 Emilija 最喜爱歌曲中的一个单词。

输入的单词都只包含小写字母,且单词长度总和不超过 \(200000\)。

输出格式

输出获胜的玩家,Nina 或 Emilija。

样例输入

2
aaa
bbb
3
aab
aba
bbb

样例输出

Nina

难度&重要性(1~10):5.5

题目来源

COCI,luogu

题目算法

字典树

解题思路

因为它要求必须是为喜欢的歌的前缀,那么我们就很容易想到建一个字典树记录所有歌。

在记录所有歌的同时,再用一个变量来表示这一段前缀是公共的,还是只有 Nina 或 Emilija 有。

例如样例数据:

2
aaa
bbb
3
aab
aba
bbb

建出的字典树如图:



这里的 \(1\) 表示的是 Nina 的前缀,\(2\) 表示的是 Emilija 的前缀,\(3\) 表示是公共前缀。

因为是回合制的游戏,所以字典树的相邻两层是由不同的人来选择的。

我们只需要在字典树上跑一个 dfs,每一次来判断上一层的人的选择是否可行。

即如果第 \(i\) 层的第 \(j\) 个字符,如果这个人可以通过选下一层的一个字符获胜,那么就说明另一个人不应该选择第 \(i\) 层的第 \(j\) 个字符。

反之如果这个人怎么选都不可以获胜,那么就说明另一个人可以通过第 \(i\) 层的第 \(j\) 个字符获胜。

Code

void make(int k){//建字典树
int len=s.size(),u=0;
For(i,0,len-1){
int c=s[i]-'a';
if(!ch[u][c].i)ch[u][c].i=++tot;
if(ch[u][c].k==0)ch[u][c].k=k;//如何没有出现过,那就只有这一个人
if(ch[u][c].k!=k)ch[u][c].k=3;//如何另一个人也有这个前缀,那这就是公共的
u=ch[u][c].i;
}
return;
} bool dfs(int u,int p){
int k=0;
For(i,0,25)
if(ch[u][i].i!=0){
if(ch[u][i].k==3)k=dfs(ch[u][i].i,!p);
if(ch[u][i].k==1&&p==0)return 0;
if(ch[u][i].k==2&&p==1)return 0;
if(k)return 0;
//如果p这个人可以获胜,即另一个人不应选u,返回0
}
return 1;
//如果p这个人怎么选都获胜不了,那么另一个人就可以选u,返回1
} signed main(){
cin>>n;
For(i,1,n){
cin>>s;
make(1);//1表示 Nina
} cin>>m;
For(i,1,m){
cin>>s;
make(2);//2表示 Emilija
} For(i,0,25)
if(ch[0][i].i!=0){
ans=dfs(ch[0][i].i,1);
//0表示 Nina
//1表示 Emilija if(ans){//如何可以获胜就输出 Nina
cout<<"Nina";
return 0;
}
}
//如何 Nina怎么选都不能获胜,就输出 Emilija
cout<<"Emilija";
return 0;
}

完成状态

已完成

vlak的更多相关文章

  1. Centos下安装LoadRunner负载机

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAd0AAADfCAIAAAA84J6GAAAgAElEQVR4nJzb93cbR6In+uK/8v6Fd8

随机推荐

  1. 2021-02-15:给定一个整型数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿。但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。

    2021-02-15:给定一个整型数组arr,代表数值不同的纸牌排成一条线.玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿.但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪 ...

  2. 2022-01-18:将数组分成两个数组并最小化数组和的差。 给你一个长度为 2 * n 的整数数组。你需要将 nums 分成 两个 长度为 n 的数组,分别求出两个数组的和,并 最小化 两个数组和之

    2022-01-18:将数组分成两个数组并最小化数组和的差. 给你一个长度为 2 * n 的整数数组.你需要将 nums 分成 两个 长度为 n 的数组,分别求出两个数组的和,并 最小化 两个数组和之 ...

  3. 安装vue cli3以及配置环境 镜像下载

    安装vue cli3以及配置环境 镜像下载 1.下载安装Node.js 安装vue cli3之前需要先安装Node.js,方便对vue进行下载 node.js的下载与安装方法在隔壁哟 地址:https ...

  4. Go语言如何判断两个对象是否相等

    1. 引言 在编程中,判断两个对象是否相等是一项常见的任务,同时判断对象是否相等在很多情况下都非常重要,例如: 单元测试:编写单元测试时,经常需要验证函数的输出是否符合预期,这涉及到比较对象是否相等. ...

  5. 如何通过CAD图中的坐标来确定是哪个坐标系

    国内常见的坐标系 坐标系分为以下两种: 地理坐标系(Geographic Coordinate System, GCS) 投影坐标系(Projected Coordinate System, PCS) ...

  6. 龙芯下如何进行.net core程序开发部署

    龙芯LoongArch64已经发布了对.NETCore的支持,相关工具链也已完成,目前支持.NETCore3.1..NET6..NET7版本.本文以.NETCore3.1在loongnix-serve ...

  7. celery笔记三之task和task的调用

    本文首发于公众号:Hunter后端 原文链接:celery笔记三之task和task的调用 这一篇笔记介绍 task 和 task 的调用. 以下是本篇笔记目录: 基础的 task 定义方式 日志处理 ...

  8. [ARM 汇编]进阶篇—异常处理与中断—2.4.1 异常处理概念

    异常处理简介 在ARM汇编开发中,异常处理和中断是常见的概念,它们是对系统运行过程中出现的特殊情况进行处理的一种机制.异常处理和中断包括硬件异常.软件异常和外部中断等.当处理器遇到这些特殊情况时,它会 ...

  9. 程序包xx不存在解决方案:java: 程序包org.aspectj.lang.annotation

    java: 程序包org.aspectj.lang.annotation不存在 #我自己已经配置好pom.xml文件,但是一直报错这个 aspectj 不存在 下面是我的pom.xml配置 可以看出来 ...

  10. 面试官:“同学,你做的这几个项目都不错。但怎么问QPS你就胡说呢?”

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 这位同学,你比上一位面试者好多了,你的简历中做的几个项目都不错.既有业务项目,也有技术项目,看 ...