Codeforces Round 973 (Div. 2)

Sol CF2013

A

每次最多操作 \(\min(x, y)\),故答案为 \(\lceil\frac{n}{\min(x, y)}\rceil\)

#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;

#define IOS
#define MULTI

void solve() {
	int n, x, y;
	cin >> n >> x >> y;
	x = min(x, y);
	cout << (n + x - 1) / x << '\n';
}

int main() {
#ifdef IOS
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
#endif
#ifdef MULTI
	int TestCase = 1;
	for (cin >> TestCase; TestCase--;) solve();
#else
	solve();
#endif
	return 0;
}

B

\(i < j < k\)\(j\)\(i\) 打败,\(k\)\(j\) 打败,最后的答案为 \(a_k + a_i - a_j\)

可知 \(n\) 必须保留,前面所有都必须被某个人打败。

\(n - 1\) 必须被 \(n\) 打败,即答案一定有 \(a_n - a_{n-1}\)

那么只需要让 \(n - 1\) 先打败 \([1, n - 2]\),就可构造最大答案 \(a_1 + a_2 + \cdots + a_{n - 2} - a_{n - 1} + a_n\)

#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;

#define IOS
#define MULTI
i < j < k

j eat i
k eat j


n + 1 + 2 + ... + (n - 2) - (n - 1)

void solve() {
	int n;
	cin >> n;
	vector<int> a(n);
	for (int &x : a)
		cin >> x;
	// sort(begin(a), end(a));
	i64 sum = 0;
	for (int i = 0; i < n; ++i)
		if (i != n - 2) sum += a[i];
	cout << sum - a[n - 2] << '\n';
}

int main() {
#ifdef IOS
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
#endif
#ifdef MULTI
	int TestCase = 1;
	for (cin >> TestCase; TestCase--;) solve();
#else
	solve();
#endif
	return 0;
}

C

首先考虑从前缀或后缀开始猜,那么每次只需要一个询问就能确定下一个字符。

考虑怎么搞到前缀/后缀。不妨一开始先随便问一个字符,然后不断在这个字符后面添加 0/1,再询问,就可以得到一个极长的存在于原串 \(s\) 中的子串 \(t\)

为什么称为极长?因为 \(t\) 后面无论添加 0 还是 1 都无法更长。那么,\(t\) 就是一个后缀!并且这个 \(t\) 只是后缀!因为假设 \(t\) 出现在了 \(s\) 中一个非后缀的位置,显然 \(t\) 不是极长的(还可以往后拓展)。

那么不断在 \(t\) 前面加 0/1 即可得到答案。注意处理边界防止询问次数过多,可参考代码。

#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;

#define IOS
#define MULTI

void solve() {
	int n;
	cin >> n;
	auto ask = [=](string s) -> int {
		int judge;
		cout << "? " << s << endl;
		cin >> judge;
		return judge;
	};
	auto guess = [=](string s) -> void {
		cout << "! " << s << endl;
		return;
	};
	auto sol = [=](string s) {
		for (int len = 2; len <= n; ++len) {
			s.push_back('0');
			if (ask(s)) continue;
			else {
				s[len - 1] = '1';
				if (ask(s)) continue;
				else {
					s.pop_back();
					break;
				}
			}
		}
		return s;
	};
	if (!ask("0")) {
		string res;
		for (int i = 1; i <= n; ++i)
			res.push_back('1');
		guess(res);
	} else {
		string suf = sol("0");
		while (int(suf.size()) < n) {
			suf = '0' + suf;
			if (!ask(suf)) suf[0] = '1';
		}
		guess(suf);
	}
}

int main() {
#ifdef IOS
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
#endif
#ifdef MULTI
	int TestCase = 1;
	for (cin >> TestCase; TestCase--;) solve();
#else
	solve();
#endif
	return 0;
}

D

看完题目发现可以模拟。从前往后处理,每次新加入一个数,我们考虑尽可能把这个数调大。当然如果这个数是 \(\max\) 就不用调了,也没办法调(所以算法是正确的)。

如何尽可能调大?就是找前面的最大值,将其减小,添加到当前这个数。如果开个 map,就可以 \(O(\log n)\) 找到这样的数,调完当前数后,把当前数塞回去。均摊一下时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;

#define IOS
#define MULTI

void solve() {
	int n;
	cin >> n;
	vector<i64> a(n);
	for (auto &x : a)
		cin >> x;
	map<i64, int> mp;
	mp[0] = 0;
	// 假设 val -= k
	// 1. k <= (val - a[i]) / (cnt + 1)
	// 2. k <= val - sev
	// cout << "n = " << n << '\n';
	for (int i = 0; i < n; ++i) {
		while (int(mp.size()) > 1) {
			auto [val, cnt] = *rbegin(mp);
			mp.erase(val);
			i64 sev = rbegin(mp) -> first;
			i64 k = min(val - sev, (val - a[i]) / (cnt + 1));
			if (k < 0) k = 0;
			a[i] += cnt * k;
			mp[val - k] += cnt;
			if (k == 0 || val == 0) break;
		}
	// 假设 cnt -= k
	// 1. k < cnt
	// 2. a[i] + cnt < val
		while (int(mp.size()) > 1) {
			auto [val, cnt] = *rbegin(mp);
			if (val == 0) break;
			mp.erase(val);
			// i64 sev = rbegin(mp) -> first;
			i64 k = min((i64)cnt, val - a[i]) - 1;
			if (k < 0) k = 0;
			a[i] += k;
			mp[val] += cnt - k;
			mp[val - 1] += k;
			if (k == 0) break;
		}
		mp[a[i]] += 1;
		// for (auto [val, cnt] : mp)
		// 	for (int i = 0; i < cnt; ++i)
		// 		cout << val << " ";
		// cout << '\n';
	}
	i64 mn = 1e18, mx = -mn;
	for (auto [v, c] : mp)
		if (c) {
			mn = min(mn, v);
			mx = max(mx, v);
		}
	cout << mx - mn << '\n';
}

int main() {
#ifdef IOS
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
#endif
#ifdef MULTI
	int TestCase = 1;
	for (cin >> TestCase; TestCase--;) solve();
#else
	solve();
#endif
	return 0;
}

E/F

不会。

posted @ 2024-09-21 21:27  lingfunny  阅读(87)  评论(0编辑  收藏  举报