Codeforces Global Round 11【ABCD】 A. Avoiding Zero B. Chess Cheater C. The Hard Work of Paparazzi D. Unshuffling a Deck

比赛链接:https://codeforces.com/contest/1427

题意

(n) 个数重新排列使得不存在为 (0) 的前缀和。

题解

计算正、负前缀和,如果二者和为 (0),则不存在满足题意的排列,否则将绝对值较大的一方排在前面即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		vector<int> a(n);
		int pos = 0, neg = 0;
		for (auto &x : a) {
			cin >> x;
			(x < 0 ? neg : pos) += x;
		}
		if (pos + neg == 0) {
			cout << "NO" << "
";
			continue;
		}
		if (pos + neg > 0) {
			sort(a.begin(), a.end(), greater<>());
		} else {
			sort(a.begin(), a.end(), less<>());
		}
		cout << "YES" << "
";
		for (int i = 0; i < n; i++) {
			cout << a[i] << " 
"[i == n - 1];
		}
	}
	return 0;
}

B. Chess Cheater

题意

给出一个长为 (n) 由 W, L 组成的字符串,如果一个 W 左侧为 W,则它提供 2 分,否则为 1 分。最多可以将 (k) 个 L 变为 W,问字符串可以得到的最大分值。

题解

本题的关键是字符串中 W 的有无及两两构成的封闭区间长度。

  • 如果全为 L,则字符串的最大分值为 (max(2k-1, 0))
  • 如果存在 W,则每次操作都会至少增加 2 分,如果操作的为两个 W 区间内的最后一个 L,则会额外再增加 1 分。
    所以计算字符串的初始分值,加上 (2 imes min(k, cntL)) 分,然后区间长度排序,每当可以减去一个完整区间长就再加上 1 分。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;
	cin >> t;
	while (t--) {
		int n, k;
		cin >> n >> k;
		string s;
		cin >> s;
		int cntL = count(s.begin(), s.end(), 'L');
		if (cntL == n) {
			cout << max(2 * k - 1, 0) << "
";
			continue;
		}
		vector<int> posW;
		for (int i = 0; i < n; i++)
			if (s[i] == 'W') posW.push_back(i);
		vector<int> seg;
		for (int i = 1; i < int(posW.size()); i++)
			if (posW[i] - posW[i - 1] - 1 > 0) seg.push_back(posW[i] - posW[i - 1] - 1);
		int ans = s[0] == 'W';
		for (int i = 1; i < n; i++) 
			if (s[i] == 'W') ans += s[i - 1] == 'W' ? 2 : 1;
		ans += 2 * min(k, cntL);
		sort(seg.begin(), seg.end());
		for (auto len : seg) if (k >= len) k -= len, ans += 1;
		cout << ans << "
";
	}
	return 0;
}

C. The Hard Work of Paparazzi

题意

(r) 行与 (r) 列相交形成了 (r imes r) 个点,初始时刻记者位于左下角的 ((1,1)) 处,接下来给出 (n) 个名人的出现时间和位置,出现时间严格递增,问记者最多可以拍到多少名人的照片。
Codeforces Global Round 11【ABCD】
A. Avoiding Zero
B. Chess Cheater
C. The Hard Work of Paparazzi
D. Unshuffling a Deck

题解

This is a classical dynamic-programming task with a twist.
这是一个有些变化的经典动态规划问题。

与最长上升子序列问题的不同之处的是,本题判断条件由 (a_j > a_i) 变为了 (dis_{ij} le t_j - t_i) 以及利用 (r)(O_{(n^2)}) 优化至了 (O_{(nr)})

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int r, n;
	cin >> r >> n;
	//如果 r == 1,此时只有 1 个点
	if (r == 1) {
		cout << n << "
";
		return 0;
	}
	vector<int> t(n + 1), x(n + 1), y(n + 1);
	t[0] = 0, x[0] = 1, y[0] = 1;
	for (int i = 1; i <= n; i++) 
		cin >> t[i] >> x[i] >> y[i];
	vector<int> dp(n + 1, -1e9), mx_dp(n + 1);
	dp[0] = 0; //初始时只有时刻 0 的 (1,1) 可达
	for (int i = 1; i <= n; i++) {
		//继承之前可达的 dp 状态
		for (int j = max(i - 2 * (r - 1), 0); j < i; j++) {
			if (abs(x[i] - x[j]) + abs(y[i] - y[j]) <= t[i] - t[j])
				dp[i] = max(dp[i], dp[j] + 1);
		}
		//如果 i 大于等于最长路径,那么对于 dp[0] ~ dp[i - 2 * (r - 1)] 一定是可达的
		if (i >= 2 * (r - 1)) dp[i] = max(dp[i], mx_dp[i - 2 * (r - 1)] + 1);
		mx_dp[i] = max(dp[i], mx_dp[i - 1]);
	}
	cout << mx_dp[n] << "
";
	return 0;
}

D. Unshuffling a Deck

题意

给出一个大小为 (n) 的排列,每次操作可以将 (n) 个数分为 (1 sim n) 个非空连续份,然后将对称的份两两交换,试给出在 (n) 次操作内将排列排为升序的操作过程。

题解

  1. 找到值相差为 (1) 的逆序对:(i<j)(a_i = a_j + 1)
  2. 将已为升序的数视为一个整体,找到 (t) 满足:(i le t < j)(a_t > a_{t+1})
  3. 分为 (4) 份,(D_1=[a_1,a_2,dots,a_{i-1}], D_2=[a_i,a_{i+1},dots, a_t], D_3=[a_{t+1},a_{t+2},dots, a_j], D_4=[a_{j+1},a_{j+2},dots, a_n])
  4. 将对称组交换,转至步骤 (1)

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n;
	cin >> n;
	vector<int> a(n), pos(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		--a[i];
	}
	vector<vector<int>> ans;
	while (not is_sorted(a.begin(), a.end())) {
		for (int i = 0; i < n; i++) {
			pos[a[i]] = i;
		}
		//1
		for (int i = 1; i < n; i++) {
			if (pos[i] < pos[i - 1]) {
				//2
				int l = pos[i];
				int r = pos[i - 1];
				int mid = l;
				while (a[mid + 1] == a[mid] + 1) ++mid;
				//3
				ans.push_back({l, mid - l + 1, r - mid, n - r - 1});
				//4
				vector<int> b;
				for (int i = r + 1; i < n; i++) b.push_back(a[i]);
				for (int i = mid + 1; i < r + 1; i++) b.push_back(a[i]);
				for (int i = l; i < mid + 1; i++) b.push_back(a[i]);
				for (int i = 0; i < l; i++) b.push_back(a[i]);
				a.swap(b);
				break;
			}
		}
	}
	cout << ans.size() << "
";
	for (auto &v : ans) {
		//每份非空
		while (v.back() == 0) v.pop_back();
		while (v.front() == 0) v.erase(v.begin());
		cout << v.size() << "
";
		for (int i = 0; i < int(v.size()); i++) {
			cout << v[i] << " 
"[i == int(v.size()) - 1];
		}
	}
	return 0;
}