Educational Codeforces Round 182 (Rated for Div. 2)


A. Cut the Array

题意:把数组分成三段,使得每段和模\(3\)后的值都相同或者都不相同。

\(n\)很小,暴力枚举分段就行了。

点击查看代码
#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::vector<int> sum(n + 1);
	for (int i = 0; i < n; ++ i) {
		sum[i + 1] = sum[i] + a[i];
	}

	for (int l = 1; l <= n; ++ l) {
		for (int r = l + 1; r < n; ++ r) {
			std::set<int> s;
			s.insert(sum[l] % 3);
			s.insert(((sum[r] - sum[l]) % 3 + 3) % 3);
			s.insert(((sum[n] - sum[r]) % 3 + 3) % 3);
			if (s.size() == 1 || s.size() == 3) {
				std::cout << l << " " << r << "\n";
				return;
			}
		}
	}
	std::cout << 0 << " " << 0 << "\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. Maximum Cost Permutation

题意:一个排列,有些地方给出了,有些地方是\(0\)代表没给出。一个排列的价值为一个长度最小的子区间,使得排序这个子区间后使得数组升序。求所有合法的排列中价值最大的。

一个完全给出的排列,就是从前往后找第一个\(p_i\ne i\)的位置和最后一个这样的位置。答案就是这两个位置的区间长度。
那么现在有些地方没填,如果\(p_i\)等于\(0\)且这个地方不是必须填\(i\),则这个地方可以使得\(p_i \ne i\),发现必须填\(i\)的情况为只有一个\(0\)的时候才有可能出现,否则一定可以使得\(p_i \ne i\)。分类讨论一下。

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

using i64 = long long;

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

	int cnt = std::ranges::count(a, -1);

	int l = 0;
	while (l < n) {
		if (a[l] != -1 && a[l] == l) {
			++ l;
		} else {
			break;
		}
	}

	int r = n - 1;
	while (r >= 0) {
		if (a[r] != -1 && a[r] == r) {
			-- r;
		} else {
			break;
		}
	}

	if (cnt == 0) {
		l = n;
		for (int j = 0; j < n; ++ j) {
			if (a[j] != j) {
				l = j;
				break;
			}
		}

		r = 0;
		for (int j = n - 1; j >= 0; -- j) {
			if (a[j] != j) {
				r = j;
				break;
			}
		}

		std::cout << std::max(0, r - l + 1) << "\n";
		return;
	}

	if (cnt == 1) {
		for (int i = 0; i < n; ++ i) {
			if (a[i] == -1 && !st[i]) {
				l = n;
				for (int j = 0; j < n; ++ j) {
					if (i != j && a[j] != j) {
						l = j;
						break;
					}
				}

				r = 0;
				for (int j = n - 1; j >= 0; -- j) {
					if (i != j && a[j] != j) {
						r = j;
						break;
					}
				}

				std::cout << std::max(0, r - l + 1) << "\n";
				return;
			}
		} 
	}

	std::cout << std::max(0, r - l + 1) << "\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. Non-Descending Arrays

题意:两个数组\(a, b\)。你可以选择一些下标,使得这些下标对应的位置交换\(a_i, b_i\)。最后要使得两个数组都是升序。求方案数。

显然\(i\)只有交换和不交换两个状态。那么记\(f[i][0/1]\)表示第\(i\)个位置交换/不交换使得前缀升序的方案数,那么考虑\(i-1\)交不交换,看两个位置是否合法。

代码省略取模类。

点击查看代码
constexpr int P = 998244353;
using Z = MInt<P>;

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

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

	std::vector f(n, std::array<Z, 2>{});
	f[0][0] = f[0][1] = 1;
	for (int i = 1; i < n; ++ i) {
		if (a[i] >= a[i - 1] && b[i] >= b[i - 1]) {
			f[i][0] += f[i - 1][0];
		}

		if (a[i] >= b[i - 1] && b[i] >= a[i - 1]) {
			f[i][0] += f[i - 1][1];
		}

		if (b[i] >= a[i - 1] && a[i] >= b[i - 1]) {
			f[i][1] += f[i - 1][0];
		}

		if (b[i] >= b[i - 1] && a[i] >= a[i - 1]) {
			f[i][1] += f[i - 1][1];
		}
	}
	std::cout << f[n - 1][0] + f[n - 1][1] << "\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;
}

D. Price Tags

题意:两个数组的差异为对于每个数\(x\),两个数组的\(x\)的数量的个数差之和。给你\(n\)个数,你要选择一个大于\(1\)的正整数\(x\),使得每个\(a_i = \lceil \frac{a_i}{x} \rceil\)。然后价值为操作后的数组的和加上操作前数组和操作后数组的差异乘\(y\)。求最大价值。

\(max\)\(a\)中最大值,那么对于一个\(x\),所有数操作后的值域都在\([1, \lceil \frac{max}{x} \rceil]\)之间,那么考虑枚举\(x\),对于一个值\(i\),除\(x\)向上取整后会变成\(i\)的数的范围为\([(i - 1) \times x + 1, i\times x]\),用前缀和记录这个范围原本有多少数,就可以得到和原本\(i\)的个数的差异。

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

using i64 = long long;

void solve() {
	int n;
	i64 y;
	std::cin >> n >> y;
	const int N = 2e5;
	std::vector<int> sum(N + 1), c(N + 1);
    int max = 0;
	for (int i = 0; i < n; ++ i) {
		int x;
		std::cin >> x;
		++ c[x];
        max = std::max(max, x);
	}

    for (int i = 1; i <= N; ++ i) {
        sum[i] = c[i] + sum[i - 1];
    }

    if (max == 1) {
        std::cout << n << "\n";
        return;
    }

    i64 ans = -1e18;
    for (int x = 2; x <= max; ++ x) {
        int up = std::ceil(1.0 * max / x);
        i64 v = 0;
        for (int i = 1; i <= up; ++ i) {
            int l = (i - 1) * x + 1, r = std::min(max, i * x);
            if (l > max) {
                break;
            }
            int cnt = sum[r] - sum[l - 1];
            v += (i64)cnt * i;
            v -= std::max(0, cnt - c[i]) * y;
        }
        ans = std::max(ans, v);
    }
    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;
}

E1. Looking at Towers (easy version)

题意:对于一个数组\(a\),定义\(L(a)\)为所有\(i\)满足\(a_i > pre_{i-1}\)\(a_i\)构造的序列,其中\(pre_i\)表示前\(i\)个数的最大值,同理\(R(a)\)表示满足\(a_i> suf_{i+1}\)\(a_i\)构成的序列,其中\(suf_i\)表示后缀最大值。求\(a\)有多少个子序列\(a'\)满足\(L(a') = L(a), R(a') = R(a)\)

求出\(L(a)\)。记\(fl[i][j]\)为前\(i\)个数已经填了\(L(a)\)的前\(j\)个数的方案数,\(gl[i][j]\)表示前\(i\)个数\(a_i\)必须选填了\(L(a)\)\(j\)个数的方案数。则讨论\(a_i\)是否加入到子序列里来转移。同理记\(fr[i][j], gr[i][j]\)\(i\)\(n\)的方案数。
\(L(a)\)长度为\(ml\)\(R(a)\)长度为\(mr\),那么考虑第一个\(a_i = max\)\(i\),如果\(L(a')\)选择了这个位置,那么如果\(R(a')\)也选择了,则有\(gl[i][ml] \times fr[i + 1][mr]\)贡献,如果\(R(a')\)不选,则枚举\(R(a')\)最后一个选择的地方,即\(\sum_{j=i+1}^{n} gl[i][ml] \times gr[j][mr]\)。如果不选两个都不包含\(a_i\),则枚举后面的\(max\),让\(L(a')\)不包含这个位置,\(R(a')\)包含这个位置,也就是\(\sum_{j=i+1}^{n} [a_j == max](fl[j][ml - 1] \times gr[j][mr])\)

代码省略取模类。

点击查看代码
constexpr int P = 998244353;
using Z = MInt<P>;

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

	int max = std::ranges::max(a);
	std::vector<int> L, R;
	for (int i = 1; i <= n; ++ i) {
		if (L.empty() || a[i] > L.back()) {
			L.push_back(a[i]);
		}
	}

	for (int i = n; i >= 1; -- i) {
		if (R.empty() || a[i] > R.back()) {
			R.push_back(a[i]);
		}
	}

	int ml = L.size();
    std::vector fl(n + 1, std::vector<Z>(ml + 1));
	std::vector gl(n + 1, std::vector<Z>(ml + 1));
	fl[0][0] = 1;
	for (int i = 1; i <= n; ++ i) {
		for (int j = 0; j <= ml; ++ j) {
			fl[i][j] += fl[i - 1][j];
			if (j && a[i] <= L[j - 1]) {
				fl[i][j] += fl[i - 1][j];
                gl[i][j] += fl[i - 1][j];
			}

			if (j && a[i] == L[j - 1]) {
				fl[i][j] += fl[i - 1][j - 1];
                gl[i][j] += fl[i - 1][j - 1];
			}
		}
	}	

	int mr = R.size();
    std::vector fr(n + 2, std::vector<Z>(mr + 1));
	std::vector gr(n + 2, std::vector<Z>(mr + 1));
	fr[n + 1][0] = 1;
	for (int i = n; i >= 1; -- i) {
		for (int j = 0; j <= mr; ++ j) {
			fr[i][j] += fr[i + 1][j];
			if (j && a[i] <= R[j - 1]) {
                fr[i][j] += fr[i + 1][j];
				gr[i][j] += fr[i + 1][j];
			}

			if (j && a[i] == R[j - 1]) {
                fr[i][j] += fr[i + 1][j - 1];
				gr[i][j] += fr[i + 1][j - 1];
			}
		}
	}

	Z ans = 0;
	for (int i = 1; i <= n; ++ i) {
		if (a[i] == max) {
            ans += gl[i][ml] * (fr[i + 1][mr] + fr[i + 1][mr - 1]);
            for (int j = i + 1; j <= n; ++ j) {
                if (a[j] == max) {
                   ans += fl[j][ml - 1] * gr[j][mr];
                }
            }
            break;
        }
	}
	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;
}
posted @ 2025-09-16 00:58  maburb  阅读(726)  评论(4)    收藏  举报