Codeforces Round 1063 (Div. 2)


A. Souvlaki VS. Kalamaki

题意:一个数组\(a\),你可以将它重排。然后从左到右操作,奇数位置你操作,偶数位置另一个人操作。每次可以选择交换\(a_i, a_{i+1}\)或者不操作。你想使得数组升序,另一个不想。求能不能使得数组升序。

考虑排序后的\(a\),对于每个偶数位置\(i\),如果\(a_i \ne a_{i+1}\),则后手总有办法使得\(a_i > a_{i+1}\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	std::ranges::sort(a);
	for (int i = 1; i + 1 < n; i += 2) {
		if (a[i] != a[i + 1]) {
			std::cout << "NO\n";
			return;
		}
	}
	std::cout << "YES\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

B. Siga ta Kymata

题意:开始有一个全\(0\)\(01\)\(s\),以及一个排列。你想把\(s\)变换,使得对于另一个给出的\(01\)\(x\),满足\(x_i = 1\)的地方\(s_i = 1\)\(x_i = 0\)的地方没有要求。每次操作可以选择\(l, r\),对于\(i \in [l, r]\)\(\min(p_l, p_r) < p_i < \max(p_l, p_r)\)的每个\(i\)使得\(s_i = 1\)。最多\(5\)次操作,求方案。

观察到第一个位置和最后一个位置不可能被覆盖,且对于\(p_i = 1, p_i = n\)的位置,我们也无法覆盖。所以如果这些位置是\(1\)则无解。
否则记\(pos_1, pos_n\)\(1, n\)在排列里的位置。
给出\([1, pos_1], [1, pos_n], [pos_1, n], [pos_n, n], [\min(pos_1, pos_n), \max(pos_1, pos_n)]\)\(5\)个操作就一定能使得其它位置变成\(1\)
因为对于\((1, \min(pos_1, pos_n))\)这些位置,都变成了\(1\),对于\((\max(pos_1, pos_n), n)\)这些位置,也都变成了\(1\)。最后对于\((pos_1, pos_n)\)之间的位置,也会变成\(1\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::vector<int> p(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> p[i];
	}
	std::string s;
	std::cin >> s;
	for (int i = 0; i < n; ++ i) {
		if (s[i] == '1') {
			if (i == 0 || i == n - 1 || p[i] == 1 || p[i] == n) {
				std::cout << -1 << "\n";
				return;
			}
		}
	}

	int p1 = std::ranges::find(p, 1) - p.begin() + 1;
	int pn = std::ranges::find(p, n) - p.begin() + 1;
	if (p1 > pn) {
		std::swap(p1, pn);
	}
	std::cout << 5 << "\n";
	std::cout << 1 << " " << p1 << "\n";
	std::cout << 1 << " " << pn << "\n";
	std::cout << p1 << " " << n << "\n";
	std::cout << pn << " " << n << "\n";
	std::cout << p1 << " " << pn << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

C. Monopati

题意:一个\(2\times n\)的矩阵,每个位置都有一个\([1, 2n]\)之间的元素,你从\((1, 1)\)出发,每次可以向右或者向下走。求有多少\([l, r]\)满足\(1\leq l \leq r \leq 2n\)且如果只使用\([l, r]\)之间的数可以从\((1, 1)\)走到\((2, n)\)

只有一次向下的机会,记在\(k\)列向下,那么走过的位置就是\(a_{1, 1}, a_{1,2}, ..., a_{1, k}\)\(a_{2, k}, a_{2, k+1}, ..., a_{2, n}\)。那么\([l, r]\)需要包含其中的最小值和最大值。
则可以预处理第一行的前缀最小最大值和第二行的后缀最小最大值。记每个\(k\)对于的区间为\([l_k, r_k]\),那么对于\(l \leq l_k, r_k \leq r\)的区间就是可行的。
可以记录每个\(r\)对应的最大的\(l\),那么比它大的\(r\)最大也能取到它的\(r\)。取一下前缀\(max\)就可以知道每个\(r\)可以选多少\(l\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
    std::cin >> n;
    std::vector<int> a1(n + 1), a2(n + 1);
    for (int i = 1; i <= n; ++ i) {
        std::cin >> a1[i];
    }

    for (int i = 1; i <= n; ++ i) {
        std::cin >> a2[i];
    }

    constexpr int inf = 1e9;
    std::vector<int> pre_min(n + 1), pre_max(n + 1);
    pre_min[0] = inf;
    pre_max[0] = -inf;
    for (int i = 1; i <= n; ++ i) {
        pre_min[i] = std::min(pre_min[i - 1], a1[i]);
        pre_max[i] = std::max(pre_max[i - 1], a1[i]);
    }

    std::vector<int> suf_min(n + 2), suf_max(n + 2);
    suf_min[n + 1] = inf;
    suf_max[n + 1] = -inf;
    for (int i = n; i >= 1; -- i) {
        suf_min[i] = std::min(suf_min[i + 1], a2[i]);
        suf_max[i] = std::max(suf_max[i + 1], a2[i]);
    }

    std::vector<int> R(2 * n + 1);
    for (int i = 1; i <= n; ++ i) {
        int l = std::min(pre_min[i], suf_min[i]);
        int r = std::max(pre_max[i], suf_max[i]);
        R[r] = std::max(R[r], l);
    }

    i64 ans = 0;
    for (int i = 1; i <= 2 * n; ++ i) {
        R[i] = std::max(R[i], R[i - 1]);
        ans += R[i];
    }
    std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

D1. Diadrash (Easy Version)

题意:交互题。有一个隐藏的\([0, n - 1]\)的排列,和\(q\)个区间。你需要确定哪个区间的\(mex\)最大。你最多可以询问\(\max(300, \lceil \frac{n}{2} \rceil + 2)\)次。每次问一个区间的\(mex\)

考虑二分\(mex\),求有没有区间的\(mex \geq mid\)。那么需要满足这个区间至少包含\([0, mid - 1]\)的每个数。
则可以在\(check\)里继续二分,求\([0, mid - 1]\)出现最左的前缀,和\([0, mid - 1]\)出现最右的后缀。那么可以得到\([0, mid - 1]\)这些数的左右区间,检查有没有一个区间包含这个区间。
那么总共询问\(2log_n^2\)次。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

int ask(int l, int r) {
	std::cout << "? " << l << " " << r << std::endl;
	int res;
	std::cin >> res;
	return res;
}

void solve() {
	int n, q;
	std::cin >> n >> q;
	std::vector<std::pair<int, int>> Q(q);
	for (int i = 0; i < q; ++ i) {
		std::cin >> Q[i].first >> Q[i].second;
	}

	auto check = [&](int k) -> bool {
		int lo = 1, hi = n;
		while (lo < hi) {
			int mid = lo + hi >> 1;
			if (ask(1, mid) >= k) {
				hi = mid;
			} else {
				lo = mid + 1;
			}
		}

		int R = lo;
		lo = 1, hi = n;
		while (lo < hi) {
			int mid = lo + hi + 1 >> 1;
			if (ask(mid, n) >= k) {
				lo = mid;
			} else {
				hi = mid - 1;
			}
		}

		int L = lo;
		for (auto & [l, r] : Q) {
			if (l <= L && R <= r) {
				return true;
			}
		}
		return false;
	};

	int lo = 0, hi = n;
	while (lo < hi) {
		int mid = lo + hi + 1 >> 1;
		if (check(mid)) {
			lo = mid;
		} else {
			hi = mid - 1;
		}
	}
	std::cout << "! " << lo << std::endl;
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
posted @ 2025-11-11 01:49  maburb  阅读(459)  评论(0)    收藏  举报