Codeforces Round 960 (Div
\(\Huge{ Codeforces Round 960 (Div. 2)}\)
题目链接:Codeforces Round 960 (Div. 2)
Problems A. Submission Bait
题意
给出一个数组,Alice和Bob每次可以选择一个数字,且每次选择的数字不小于上一个人选择的数字。然后将其变为0。
最后无法进行选择的人输掉比赛。
思路
Alice先手,若最大数的个数为奇数,那么Alice赢,若为偶数,考虑第二大数字的个数,以此类推。
标程
#include<bits/stdc++.h>
using namespace std;
void Solved() {
int n; cin >> n;
vector<int> a(n + 1);
for(int i = 1; i <= n; i ++ ) cin >> a[i];
sort(a.begin() + 1, a.end(), [](int n1, int n2) {
return n1 > n2;
});
int x = 0, mx = a[1], z = 1;
for(int i = 1; i <= n; i ++ ) {
if(mx == a[i]) x ++;
else {
if(x % 2 == z) {
cout << "YES\n"; return;
} else {
x = 1; mx = a[i];
}
}
}
if(x % 2 == z) cout << "YES\n";
else cout << "NO\n";
}
signed main(void) {
int ALL = 1;
cin >> ALL;
while(ALL -- ) Solved();
return 0;
}
Problems B. Array Craft
题意
给出数组b,然后给出两个定义:
- 最大前缀位置:满足\(b_1+\ldots+b_i=\max_{j=1}^{m}(b_1+\ldots+b_j)\)的最小索引\(i\)。
- 最大后缀位置:满足$$b_i+\ldots+b_m=\max_{j=1}^{m}(b_j+\ldots+b_m)$$的最小索引\(i\)。
然后给出三个整数\(n,x,y\),分别表示数组大小,最大前缀位置,最大后缀位置。
思路
根据\(x,y\)的相对位置直接模拟就行。
根据定义,\(a_X=a_y=1\),对于\(x\),从\(x\)开始的\((1\le i \le x)\)的前缀和必然大于\(1\),从\(x\)开始的\((x\le i \le n)\)的前缀和必然小于\(1\),\(y\)也同理。
标程
#include<bits/stdc++.h>
using namespace std;
void Solved() {
int n, x, y; cin >> n >> x >> y;
vector<int> a(n + 1);
a[x] = a[y] = 1;
if(x <= y) {
int x1 = 0;
for(int i = x; i >= 1; i -- ) {
if(x1 < 1) a[i] = 1, x1 ++;
else a[i] = -1, x1 --;
}
for(int i = x + 1; i < y; i ++ ) {
a[i] = -1;
}
x1 = 0;
for(int i = y; i <= n; i ++ ) {
if(x1 < 1) a[i] = 1, x1 ++;
else a[i] = -1, x1 --;
}
} else {
int x1 = 1;
for(int i = y - 1; i >= 1; i -- ) {
if(x1 >= 1) a[i] = -1, x1 --;
else a[i] = 1, x1 ++;
}
for(int i = y + 1; i < x; i ++ ) {
a[i] = 1;
}
x1 = 1;
for(int i = x + 1; i <= n; i ++ ) {
if(x1 >= 1) a[i] = -1, x1 --;
else a[i] = 1, x1 ++;
}
}
for(int i= 1 ; i <= n; i ++ ) {
cout << a[i] << ' ';
} cout << endl;
}
signed main(void) {
int ALL = 1;
cin >> ALL;
while(ALL -- ) Solved();
return 0;
}
Problems C. Mad MAD Sum
题意
给出定义:\(MAD\)(最大显示重复数)在数组中出现至少两次的最大数。具体来说,如果没有出现至少两次的数字,则\(MAD\)为\(0\)。
循环执行下列操作:
- \(sum=sum+\sum_{i=1}^{n}{a_i}\)
- \(b_i=MAD(a_1,a_2…a_n)\)
- \(a_i=b_i(1\le i \le n)\)
当数组\(a\)全为0时结束循环,求sum的大小。
思路
手动模拟一次即可发现,只需一次循环,数组a就会变为不严格的升序\(a_i\le a_{i + 1}(1\le i\le n-1)\)。
设第一次手动模拟后的数组为b_1,那么b_1中除了最后一位,其他位可能会出现单个数字的情况(即相邻两个数都不同),且现在b_i为升序,那么这一位在下一次循环后就不存在了。
但第二次循环之后,b_2除最后一位,其他位不可能出现上述情况。
b_1之所以会出现单个数字的情况,是因为循环前原数组可能是无序的。
b_2不会出现这种情况就是因为b_1是有序的。
规律可以观察下列两种情况:
| \(a:[4,5,3,3,3,3,4,5]\) | \(a:[4,4,5,5,6,6]\) |
|---|---|
| \(b_1:[0,0,0,3,3,3,4,5]\) | \(b_1:[0,4,4,5,5,6]\) |
| \(b_2:[0,0,0,0,3,3,3,3]\) | \(b_2:[0,0,4,4,5,5]\) |
| \(b_3:[0,0,0,0,0,3,3,3]\) | \(b_3:[0,0,0,4,4,5]\) |
| \(b_4:[0,0,0,0,0,0,3,3]\) | \(b_4:[0,0,0,0,4,4]\) |
| \(b_5:[0,0,0,0,0,0,0,3]\) | \(b_5:[0,0,0,0,0,4]\) |
通过规律,我们可以先处理出来前两次循环,然后直接计算即可
标程
#include<bits/stdc++.h>
using namespace std;
#define int long long
void Solved() {
int n; cin >> n;
vector<int> a(n + 1);
for(int i = 1; i <= n; i ++ ) cin >> a[i];
int mx = 0, res = 0;
for(int j = 0; j < 2; j ++ ) {
map<int, int> mp;
mx = 0;
for(int i = 1; i <= n; i ++ ) {
res += a[i];
mp[a[i]] ++;
if(mp[a[i]] >= 2) mx = max(mx, a[i]);
a[i] = mx;
}
}
for(int i = 1; i <= n; i ++ ) {
res += a[i] * (n - i + 1);
}
cout << res<< endl;
}
signed main(void) {
int ALL = 1;
cin >> ALL;
while(ALL -- ) Solved();
return 0;
}
Problems D. Grid Puzzle
题意
给出一个数字n,然后给出一个长度为\(n\)的数组\(a\)。数组a表示在一个大小为\(n\times n\)的矩阵中,第i行的前\(a_i\)各元素是黑色。
先有下列两种操作,每次只能选择一个执行:
- 选择一个\(2\times 2\)的矩阵,并将其变为白色。
- 选择任意一行,将其全变为白色。
问最少操作多少次,可以将整个\(n\times n\)的矩阵全变为白色。
思路
-
容易发现,如果当前行\(a_i > 4\),那么显然使用一次操作2最优。
-
如果相邻两行\(a_i \le 2\),那么使用操作1最优,因为可能消除下一行。
-
如果当前的\(a_i\le 4\),那么可能用操作1,也可能是操作2;此时若下一行\(a_{i+1}\le 4\),那么考虑当前使用操作1。
综上,我们可以用\(f_i\)表示前i行的最优解,\(g_i\)记录上述(3)的情况,然后进行状态转移即可。最后\(f[n]\)即为结果。
标程
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
void Solved() {
int n; cin >> n;
vector<int> a(n + 1), f(n + 1), g(n + 1, mod);;
int res = 0;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
for(int i = 1; i <= n; i ++ ) {
f[i] = f[i - 1] + (a[i] > 0); //默认都用操作2
if(a[i] <= 2) { //这类情况可以判断是否用操作1
g[i] = min(g[i], f[i - 1] + 1);
f[i] = min(f[i], g[i - 1]); //考虑将上一行的染色方式改为操作1
}
if(i + 1 <= n && a[i] <= 4 && a[i + 1] <= 4) { //这类情况用两次操作1最优
g[i + 1] = g[i - 1] + 2;
}
}
cout << f[n]<< endl;
}
signed main(void) {
int ALL = 1;
cin >> ALL;
while(ALL -- ) Solved();
return 0;
}

浙公网安备 33010602011771号