[UOJ#405/LOJ#2863][IOI2018]组合动作(交互)

Address

UOJ#405
LOJ#2863

Solution

第一次做交互题。
题意大概是给定一个未知的,由 A 、 B 、 X 、 Y 构成的长度为 N N N 的字符串 S S S ,每次可以给出一个长度不超过 4 N 4N 4N 的字符串 T T T ,查询既是 T T T 的子串又是 S S S 的前缀的最长字符串的长度。使用不超过 N + 2 N+2 N+2 次查询,还原出字符串 S S S S S S 的第一个字符不会出现多次。
(1)求 S S S 的第一个字符。
先查询串 AB ,如果查询结果不为 0 0 0 ,则 S [ 1 ] S[1] S[1] 为 A 或 B ,然后再查询串 A ,如果查询结果不为 0 0 0 S [ 1 ] S[1] S[1] 为 A 否则为 B 。如果查询串 AB 的结果为 0 0 0 S [ 1 ] S[1] S[1] 为 X 或 Y 。同样继续查询 X 进行判断。
操作数为 2 2 2
(2)已知 S [ 1... i ] S[1...i] S[1...i] ,求 S [ 1... i + 1 ] S[1...i+1] S[1...i+1] i &lt; N − 1 i&lt;N-1 i<N1 )。
即确定 S [ i + 1 ] S[i+1] S[i+1] 。我们会发现,如果暴力判断需要 2 2 2 次操作,下面需要使用一种只使用 1 1 1 次操作的方法。
显然,只使用 1 1 1 次操作的关键是要通过查询的结果把三种字符区分开。
构造字符串(以 S [ 1 ] = ′ Y ′ S[1]=&#x27;Y&#x27; S[1]=Y 为例):
S [ 1... i ] + ′ A ′ + ′ A ′ + S [ 1... i ] + ′ A ′ + ′ B ′ + S [ 1... i ] + ′ A ′ + ′ C ′ + S [ 1.. i ] + ′ B ′ S[1...i]+&#x27;A&#x27;+&#x27;A&#x27;+S[1...i]+&#x27;A&#x27;+&#x27;B&#x27;+S[1...i]+&#x27;A&#x27;+&#x27;C&#x27;+S[1..i]+&#x27;B&#x27; S[1...i]+A+A+S[1...i]+A+B+S[1...i]+A+C+S[1..i]+B
长度最多为 4 N − 1 4N-1 4N1
易得,如果 S [ i + 1 ] = ′ A ′ S[i+1]=&#x27;A&#x27; S[i+1]=A 则查询结果为 i + 2 i+2 i+2 ,如果 S [ i + 1 ] = ′ B ′ S[i+1]=&#x27;B&#x27; S[i+1]=B 则查询结果为 i + 1 i+1 i+1 ,否则为 i i i
S [ 1 ] S[1] S[1] 为其他字符时同理。
总操作次数 N − 2 N-2 N2
(3)求 S [ N ] S[N] S[N]
暴力枚举。只需要枚举除 S [ 1 ] S[1] S[1] 之外的两个字符。
当然和求 S[1] 一样也可以
操作次数 2 2 2
这样,总操作次数就是 N + 2 N+2 N+2

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include "combo.h"
#define For(i, a, b) for (i = a; i <= b; i++)

const char ch[] = {'A', 'B', 'X', 'Y'};

std::string add(std::string s, int l, int r)
{
	int i;
	For (i, l, r) s.push_back(ch[i]);
	return s;
}

std::string dda(std::string s, int l, int r)
{
	int i; std::string res = "";
	For (i, l, r) res += s, res.push_back(ch[i]);
	return res;
}

char inters(int x, int y)
{
	int i, pos = -1;
	For (i, 1, y)
	{
		pos++;
		if (pos == x) pos++;
	}
	return ch[pos];
}

std::string guess_sequence(int N)
{
	int i, firs, l = 0, r = 3;
	std::string st;
	For (i, 0, 1)
	{
		int mid = l + r >> 1;
		if (press(add(st, l, mid))) r = mid;
		else l = mid + 1;
	}
	firs = l; st.push_back(ch[l]);
	if (N == 1) return st;
	For (i, 1, N - 2)
	{
		int tmp = press(st + inters(firs, 1) + inters(firs, 1)
			+ st + inters(firs, 1) + inters(firs, 2)
			+ st + inters(firs, 1) + inters(firs, 3)
			+ st + inters(firs, 2));
		if (tmp == i + 2) st.push_back(inters(firs, 1));
		else if (tmp == i + 1) st.push_back(inters(firs, 2));
		else st.push_back(inters(firs, 3));
	}
	l = 0; r = 3;
	For (i, 0, 1)
	{
		int mid = l + r >> 1;
		if (press(dda(st, l, mid)) == N) r = mid;
		else l = mid + 1;
	}
	st.push_back(ch[l]);
	return st;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值