CodeForces 1840G In Search of Truth

发布时间 2023-06-09 08:30:32作者: zltzlt

洛谷传送门

CF 传送门

每次询问获得的信息只有当前所在位置的数字。考虑这样一件事情,如果我们询问了 \(b_1, b_2, ..., b_k\) 之后,走到了询问 \(b_1\) 前所在的数字,就说明 \(n \mid \sum\limits_{i = 1}^k b_i\)。可以考虑构造一个步数序列 \(a_1, a_2, ..., a_m\),如果能满足任何一个 \(x \in [1, 10^6]\) 都存在 \(1 \le l \le r \le m\) 使得 \(\sum\limits_{i = l}^r a_i = x\),那么就做完了。

先考虑 G1 \(2023\) 次询问怎么做。发现是 \(2 \sqrt{n}\) 级别,启发我们类似 BSGS 地,构造 \(a_1 = a_2 = \cdots = a_{1000} = 1, a_{1001} = a_{1002} = \cdots = a_{2000} = 1000\)。所用询问次数为 \(2000\)可以通过 G1

但是 G2 只能询问 \(1000\) 次。发现此时无论如何都不能构造出满足条件的 \(m \le 1000\) 的步数序列。我们还发现一件事情,就是所到达位置的数字一定 $ \le n$。那我们是不是可以先在这个圆上随机游走一会,得到 \(n\) 的一个紧一点的下界呢?发现这样是可行的。具体地,设置一个 \(B\),先随机游走 \(998 - 2B\) 次,得到经过的数字的最大值 \(k\),然后构造 \(a_1 = a_2 = \cdots = a_B = 1, a_{B + 1} = k, a_{B + 2} = a_{B + 3} = \cdots = a_{2B + 1} = B^2\) 即可。总共使用 \(999\) 次询问(不知道为什么最多用 \(999\) 次,用 \(1000\) 次就不行)。

可以发现出错的情况就是,随机 \(998 - 2B\) 次随不到 \([n - B(B + 1), n]\) 的数。取 \(B = 339\),出错概率 \(< 10^{-17}\)可以通过 G2

code
// Problem: G2. In Search of Truth (Hard Version)
// Contest: Codeforces - Codeforces Round 878 (Div. 3)
// URL: https://codeforces.com/contest/1840/problem/G2
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

inline int ask(int x) {
	if (x > 0) {
		printf("+ %d\n", x);
	} else {
		printf("- %d\n", -x);
	}
	fflush(stdout);
	scanf("%d", &x);
	return x;
}

const int maxn = 1010;

int n = 339, a[maxn], b[maxn];
mt19937 rnd(time(NULL));

void solve() {
	int k = 0;
	scanf("%d", &k);
	for (int _ = 0; _ < 998 - n * 2; ++_) {
		a[0] = ask(rnd() % 1000000000 + 1);
		k = max(k, a[0]);
	}
	for (int i = 1; i <= n; ++i) {
		a[i] = ask(1);
	}
	b[0] = ask(k);
	for (int i = 1; i <= n; ++i) {
		b[i] = ask(n);
	}
	int ans = 1e9;
	for (int i = 0; i <= n; ++i) {
		for (int j = i + 1; j <= n; ++j) {
			if (a[i] == a[j]) {
				ans = min(ans, j - i);
			}
			if (b[i] == b[j]) {
				ans = min(ans, (j - i) * n);
			}
		}
	}
	for (int i = 0; i <= n; ++i) {
		for (int j = 0; j <= n; ++j) {
			if (a[i] == b[j]) {
				ans = min(ans, n - i + j * n + k);
			}
		}
	}
	printf("! %d\n", ans);
	fflush(stdout);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}