UOJ405 【IOI2018】组合动作 题解
题目链接:#405. 【IOI2018】组合动作
题意:
你在玩一个动作游戏。游戏控制器有 $4$ 个按键,A、B、X 和 Y。在游戏中,你用组合动作来赚金币。你可以依次按这些按键来完成一个组合动作。
这个游戏有一个隐藏的按键序列,可以表示为由这 $4$ 个字符组成的串 $S$。你并不知道这个串 $S$,但是你知道它的长度为 $N(1\le N \le 2000)$。
你还知道,$S$ 的首字符不会在串中重复出现。例如,$S$ 可以是”
ABXYY
“或者”XYYAA
“,但不能是”AAAAA
“或”BXYBX
“。你可以依次按最多 $4N$ 个按键来完成一个组合动作。串 $p$ 为你所按的按键序列。你用这个组合动作赚到的金币数量,等于同时为 $p$ 之子串和 $S$ 之前缀的最长字符串的长度。串 $t$ 的子串定义为 $t$ 中的连续字符序列 (可以为空)。$t$ 的前缀定义为 $t$ 的子串,其或者为空,或者包含 $t$ 的首字符。
例如,如果 $S$ 是 “
ABXYY
“,而 $p$ 是 “XXYYABYABXAY
“,你会得到 $3$ 个金币,因为 “ABX
“ 是可作为 $p$ 的子串的 $S$ 的前缀中最长的。你的任务是,用少量的组合动作,找出隐藏字符串 $S$ 。
实现细节:
你需要实现下面的函数:
std::string guess_sequence(int N)
N
:串 $S$ 的长度。- 对每个测试用例,该函数被调用恰好一次。
- 该函数应返回串 $S$。
你的程序可以调用下面的函数:
int press(std::string p)
p
:你的按键序列。p
必须是长度为从 $0$ 到 $4N$ 的串 (包括 $0$ 和 $4N$)。p
的每个字符必须是A
、B
、X
或者Y
。- 对每个测试用例,你调用该函数的次数不能超过 $8000$ 次。
- 该函数的返回结果是,当按出按键序列
p
后你赚到的金币数量。如果不满足上面的条件,你的程序将被判为
Wrong Answer
。否则,你的程序将被判为Accepted
,而你的得分将根据press
的调用次数来计算。
有趣的构造+交互题。
从首字母入手,先花 $2$ 次询问找到第一个字符 $c$ 。
为什么要找到它呢?因为它在隐藏串中只出现在第一个。
这意味着,不存在一个 $S$ 的子串第一个字符是 $c$
于是我们可以以 $c$ 为分隔符,构造出形如 PvPwuPwvPww
的询问串( $c=$ P
,$u,v,w \ne c$ )
这个串特殊在它可以直接得到下一个字符为 $u,v,w$ 中的哪一个
因为他们对应的返回值(金币数)为 $l,~l+1,~l+2$ 。
且询问串长 $4l+7 \le 4n$ 只需 $l \le n-2$ 。然后最后一个字符直接 $2$ 次问就好了。
于是询问数 $n+2$ 。
时间复杂度 $O(n^2)$
代码:(题目只要实现函数)
#include "combo.h"
#include <bits/stdc++.h>
using namespace std;
string guess_sequence(int n)
{
char c = press("AB") ? (press("A") ? 'A' : 'B') : press("X") ? 'X' : 'Y';
char rem[3] = {'A','B','X'};
string res(1,c);
if (n == 1) return res;
for (int i=0; i<3; i++) rem[i] == c ? rem[i] = 'Y' : 0;
for (int i=1; i<n-1; i++)
{
int j=press(res + rem[1] + res + rem[2] + rem[0] + res + rem[2] + rem[1] + res + rem[2] + rem[2]);
res.push_back(rem[j - i]);
}
res.push_back(press(res + rem[0]) == n ? rem[0] : press(res + rem[1]) == n ? rem[1] : rem[2]);
return res;
}
参考文献:
[1] https://yhx-12243.github.io/OI-transit/records/uoj405%3Bloj2863.html