「解题报告」Codeforces Round #884 (Div. 1 + Div. 2) Editorial
比赛地址:Dashboard - Codeforces Round 884 (Div. 1 + Div. 2) - Codeforces
个人评价:这场是构造专场!
A. Subtraction Game
有一堆石子(应该是石子),每次只能拿走 \(a\) 块或者 \(b\) 块,最先不能移动的人输,构造一个数 \(n\),使得先手必输。
两种构造方法:
-
构造一个数 \(n\) 小于 \(\min\{a, b\}\)。
-
构造一个数 \(n\),\((a + b) \le n \ < (a + b + \min\{a, b\})\)。
其实就是变相的 a + b
/*
The code was written by yifan, and yifan is neutral!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
int T;
void solve() {
ll a = read<ll>(), b = read<ll>();
cout << a + b << '\n';
}
int main() {
T = read<int>();
while (T --) {
solve();
}
return 0;
}
B. Permutations & Primes
给定一个整数 \(n\),要求构造一个 \(1 \sim n\) 的排列,使得这个排列的素数值最大。
素数值定义:若一个子区间的 \(\operatorname{mex}\) 是一个质数,那么素数值加一。
\(\operatorname{mex}\):一个区间中最小的没有出现的非负整数,但在本题中不讨论 \(0\),因此在本题中的含义是最小的没有出现的正整数。
构造方法:
-
\(1\) 放在中间。如果一个区间不包括 \(1\),那么这个区间对素数值的贡献为 \(0\),想让贡献最大,\(1\) 放在中间。
-
第一个位置放 \(2\),最后一个位置放 \(3\)。只要包括 \(1, 2\),但不包括 \(3\),则 \(\operatorname{mex} = 3\),是质数;包括 \(1, 3\),但不包括 \(2\),则 \(\operatorname{mex} = 2\),也是质数;当同时包括 \(1, 2, 3\) 时,区间长度为 \(n\),即 \(\operatorname{mex} = n + 1\),对于所有的排列,都会有这个最大的区间,因此就可以不考虑它,即可以不考虑同时包括 \(1, 2, 3\) 的情况。
-
剩下的空位随便排
/*
The code was written by yifan, and yifan is neutral!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 2e5 + 5;
int T;
int ans[N];
void work() {
int n = read<int>();
for (int i = 1; i <= n; ++ i) {
ans[i] = 0;
}
ans[1] = 2, ans[n] = 3, ans[n / 2 + 1] = 1;
// 这里这样写就不用特判 1 的情况了
for (int i = 2, k = 4; i < n; ++ i) {
if (!ans[i]) {
ans[i] = k;
++ k;
}
}
for (int i = 1; i <= n; ++ i) {
cout << ans[i] << " ";
}
putchar('\n');
}
int main() {
T = read<int>();
while (T --) {
work();
}
return 0;
}
C. Particles
给定一个 \(n\) 个整数(可以为负)的序列,可以从中删掉某个位置的数,然后把这个位置的左右两边的数加在一起,合成一个新的元素(两个元素合为一个,数量也减一),如果左右元素为空,就不合并。按此操作直到最后只剩一个数,求最大可能的数。
将奇数位置的数和偶数位置的数各自提取出来,可以发现,只有奇数位置的数能与奇数位置的数合并,同理,也只有偶数位置的数可以和偶数位置的数合并(后面简称奇数集和偶数集),即最后的答案一定为奇数集的和或偶数集的和。
对于负数的情况,我们总可以将负数都删去,如果存在一个负数删掉后左右两边出现了一个正数或一个负数,那么一定会使奇数集或偶数集的答案减小,至于让哪个集的和减小,哪个集的和成为答案的可能性小,就让哪个集的和减小。
对于全是负数的极端情况,输出序列的最大值即可。
/*
The code was written by yifan, and yifan is neutral!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 2e5 + 5;
int n;
int a[N];
void solve() {
n = read<int>();
bool fg = 1;
ll ans1 = 0, ans2 = 0;
for (int i = 1; i <= n; ++ i) {
a[i] = read<int>();
fg &= (a[i] < 0);
}
if (fg) {
cout << *max_element(a + 1, a + n + 1) << '\n';
return ;
}
for (int i = 1; i <= n; ++ i) {
if (i & 1) {
ans1 += max(a[i], 0);
}
else {
ans2 += max(a[i], 0);
}
}
cout << max(ans1, ans2) << '\n';
return ;
}
int main() {
int T = read<int>();
while (T --) {
solve();
}
return 0;
}
D. Row Major
非正解 暴力跑过去了?
构造一个字符串,长度为 \(n\),该字符串可以一次转化成一个 \(r \times c\) 的矩阵(即 \(r \times c = n\)),是的矩阵中相邻元素相等的情况最少。
首先,在序列中相邻的元素不能相同,之后枚举 \(n\) 的因数,判断矩阵中上下相邻的元素是否相同即可。
/*
The code was written by yifan, and yifan is neutral!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 1e6 + 5;
vector<int> y;
char s[N];
bool vis[127];
void solve() {
y.clear();
memset(s, '\0', sizeof s);
int n = read<int>();
int lim = sqrt(n);
for (int i = 2; i <= lim; ++ i) {
if (n % i == 0) {
y.emplace_back(i);
y.emplace_back(n / i);
}
}
s[1] = 'a';
for (int i = 2; i <= n; ++ i) {
for (int j = 'a'; j <= 'z'; ++ j) {
vis[j] = 0;
}
int tmp = 'a';
vis[(int)s[i - 1]] = 1;
while (vis[tmp]) ++ tmp;
for (int j : y) {
if (i - j <= 0) continue ;
vis[(int)s[i - j]] = 1;
while (vis[tmp]) {
++ tmp;
}
if (tmp > 'z') tmp = 'z';
}
s[i] = tmp;
}
for (int i = 1; i <= n; ++ i) {
putchar(s[i]);
}
putchar('\n');
}
int main() {
int T = read<int>();
while (T --) {
solve();
}
return 0;
}
E 往后,题解就看不懂了,就不再补了。