CodeForces 1286C2 Madhouse (Hard version)

洛谷传送门

CF 传送门

可以把限制看成 \(0.75n^2\)。发现 \(0.75n^2 = 0.5n^2 + 2 \times 0.5 (\frac{n}{2})^2\)。这启发我们询问一次 \([1, n]\) 和两次长度为 \(\frac{n}{2}\) 的区间。

不妨问 \([1, n], [1, \frac{n}{2}], [1, \frac{n}{2} + 1]\) 试试。注意到把 \([1, \frac{n}{2} + 1]\) 得到的集合除去 \([1, \frac{n}{2}]\) 中的元素,得到的是左端点在 \([1, \frac{n}{2} + 1]\),右端点为 \(\frac{n}{2} + 1\) 的子串,可以通过这些串中长度为 \(i\) 除去长度为 \(i - 1\) 的串剩下的字符求出原串 \([1, \frac{n}{2} + 1]\) 中的字符。接下来的任务是求 \([\frac{n}{2} + 2, n]\) 中的字符。

考虑 \([1, n]\) 的集合中所有长度为 \(2\) 的串,除了已知的 \(s[1, \frac{n}{2} + 1]\),剩下的只有 \(s_n\) 出现 \(1\) 次,\(s[\frac{n}{2} + 2, n - 1]\) 都是出现 \(2\) 次。那么出现次数为奇数的字母就是 \(s_n\)

再考虑 \([1, n]\) 中所有长度为 \(3\) 的串。除了已知的 \(s[1, \frac{n}{2} + 1]\)\(s_n\),剩下的只有 \(s_{n - 1}\) 出现 \(2\) 次,\(s[\frac{n}{2} + 2, n - 2]\) 都是出现 \(3\) 次。那么出现次数 \(\bmod\ 3 = 2\) 的字母就是 \(s_n\)

以此类推,所有长度为 \(i\) 的串中,除去已知的,只有 \(s_{n - i + 2}\) 出现 \(i - 1\) 次,其他未知的都出现 \(i\) 次。所以出现次数 \(\bmod\ i = i - 1\) 的字母就是 \(s_{n - i + 2}\)。这样可以确定 \([\frac{n}{2} + 2, n]\) 的部分。

总共使用 \(3\) 次询问,总子串个数 \(\approx 0.75n^2\)

code
// Problem: C2. Madhouse (Hard version)
// Contest: Codeforces - Codeforces Round 612 (Div. 1)
// URL: https://codeforces.com/problemset/problem/1286/C2
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#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;

const int maxn = 110;

int n, a[26];
string b[maxn];
char ans[maxn];
vector<string> vc[maxn];

inline multiset<string> ask(int l, int r) {
	int n = r - l + 1;
	printf("? %d %d\n", l, r);
	fflush(stdout);
	multiset<string> S;
	for (int _ = 0; _ < n * (n + 1) / 2; ++_) {
		string s;
		cin >> s;
		sort(s.begin(), s.end());
		S.insert(s);
	}
	return S;
}

void solve() {
	scanf("%d", &n);
	if (n <= 3) {
		string ans = "";
		for (int i = 1; i <= n; ++i) {
			ans += *ask(i, i).begin();
		}
		printf("! %s\n", ans.c_str());
		return;
	}
	auto A = ask(1, n), B = ask(1, n / 2), C = ask(1, n / 2 + 1);
	auto D = C;
	for (auto s : B) {
		D.erase(D.find(s));
	}
	int tot = 0;
	for (auto s : D) {
		b[++tot] = s;
	}
	sort(b + 1, b + tot + 1, [&](const string &a, const string &b) {
		return a.size() > b.size();
	});
	ans[tot] = b[tot][0];
	for (int i = tot - 1; i; --i) {
		mems(a, 0);
		for (char c : b[i]) {
			++a[c - 'a'];
		}
		for (char c : b[i + 1]) {
			--a[c - 'a'];
		}
		for (int j = 0; j < 26; ++j) {
			if (a[j]) {
				ans[i] = 'a' + j;
				break;
			}
		}
	}
	for (auto s : C) {
		A.erase(A.find(s));
	}
	for (auto s : A) {
		vc[(int)s.size()].pb(s);
	}
	for (int i = 2; i <= n - n / 2; ++i) {
		mems(a, 0);
		for (auto s : vc[i]) {
			for (char c : s) {
				++a[c - 'a'];
			}
		}
		for (int j = n / 2 + 1, k = i - 1; k; --j, --k) {
			a[ans[j] - 'a'] -= k;
		}
		for (int j = n, k = 1; k <= i - 2; --j, ++k) {
			a[ans[j] - 'a'] -= k;
		}
		for (int j = 0; j < 26; ++j) {
			if (a[j] % i == i - 1) {
				ans[n - i + 2] = 'a' + j;
				break;
			}
		}
	}
	printf("! %s\n", ans + 1);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2024-02-09 17:26  zltzlt  阅读(20)  评论(0)    收藏  举报