C o d e f o r c e s R o u n d 960 ( D i v . 2 ) \Huge{ Codeforces Round 960 (Div. 2)} CodeforcesRound960(Div.2)
文章目录
- Problems A. Submission Bait
- 题意
- 思路
- 标程
- Problems B. Array Craft
- 题意
- 思路
- 标程
- Problems C. Mad MAD Sum
- 题意
- 思路
- 标程
- Problems D. Grid Puzzle
- 题意
- 思路
- 标程
题目链接: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 + … + b i = max j = 1 m ( b 1 + … + b j ) b_1+\ldots+b_i=\max_{j=1}^{m}(b_1+\ldots+b_j) b1+…+bi=maxj=1m(b1+…+bj)的最小索引 i i i。
- 最大后缀位置:满足 b i + … + b m = max j = 1 m ( b j + … + b m ) b_i+\ldots+b_m=\max_{j=1}^{m}(b_j+\ldots+b_m) bi+…+bm=j=1maxm(bj+…+bm)的最小索引 i i i。
然后给出三个整数 n , x , y n,x,y n,x,y,分别表示数组大小,最大前缀位置,最大后缀位置。
思路
根据 x , y x,y x,y的相对位置直接模拟就行。
根据定义, a X = a y = 1 a_X=a_y=1 aX=ay=1,对于 x x x,从 x x x开始的 ( 1 ≤ i ≤ x ) (1\le i \le x) (1≤i≤x)的前缀和必然大于 1 1 1,从 x x x开始的 ( x ≤ i ≤ n ) (x\le i \le n) (x≤i≤n)的前缀和必然小于 1 1 1, y y 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
题意
给出定义: M A D MAD MAD(最大显示重复数)在数组中出现至少两次的最大数。具体来说,如果没有出现至少两次的数字,则 M A D MAD MAD为 0 0 0。
循环执行下列操作:
- s u m = s u m + ∑ i = 1 n a i sum=sum+\sum_{i=1}^{n}{a_i} sum=sum+∑i=1nai
- b i = M A D ( a 1 , a 2 … a n ) b_i=MAD(a_1,a_2…a_n) bi=MAD(a1,a2…an)
- a i = b i ( 1 ≤ i ≤ n ) a_i=b_i(1\le i \le n) ai=bi(1≤i≤n)
当数组 a a a全为0时结束循环,求sum的大小。
思路
手动模拟一次即可发现,只需一次循环,数组a就会变为不严格的升序 a i ≤ a i + 1 ( 1 ≤ i ≤ n − 1 ) a_i\le a_{i + 1}(1\le i\le n-1) ai≤ai+1(1≤i≤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,5,3,3,3,3,4,5] a:[4,5,3,3,3,3,4,5] | a : [ 4 , 4 , 5 , 5 , 6 , 6 ] a:[4,4,5,5,6,6] a:[4,4,5,5,6,6] |
---|---|
b 1 : [ 0 , 0 , 0 , 3 , 3 , 3 , 4 , 5 ] b_1:[0,0,0,3,3,3,4,5] b1:[0,0,0,3,3,3,4,5] | b 1 : [ 0 , 4 , 4 , 5 , 5 , 6 ] b_1:[0,4,4,5,5,6] b1:[0,4,4,5,5,6] |
b 2 : [ 0 , 0 , 0 , 0 , 3 , 3 , 3 , 3 ] b_2:[0,0,0,0,3,3,3,3] b2:[0,0,0,0,3,3,3,3] | b 2 : [ 0 , 0 , 4 , 4 , 5 , 5 ] b_2:[0,0,4,4,5,5] b2:[0,0,4,4,5,5] |
b 3 : [ 0 , 0 , 0 , 0 , 0 , 3 , 3 , 3 ] b_3:[0,0,0,0,0,3,3,3] b3:[0,0,0,0,0,3,3,3] | b 3 : [ 0 , 0 , 0 , 4 , 4 , 5 ] b_3:[0,0,0,4,4,5] b3:[0,0,0,4,4,5] |
b 4 : [ 0 , 0 , 0 , 0 , 0 , 0 , 3 , 3 ] b_4:[0,0,0,0,0,0,3,3] b4:[0,0,0,0,0,0,3,3] | b 4 : [ 0 , 0 , 0 , 0 , 4 , 4 ] b_4:[0,0,0,0,4,4] b4:[0,0,0,0,4,4] |
b 5 : [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 3 ] b_5:[0,0,0,0,0,0,0,3] b5:[0,0,0,0,0,0,0,3] | b 5 : [ 0 , 0 , 0 , 0 , 0 , 4 ] b_5:[0,0,0,0,0,4] b5:[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 n n的数组 a a a。数组a表示在一个大小为 n × n n\times n n×n的矩阵中,第i行的前 a i a_i ai各元素是黑色。
先有下列两种操作,每次只能选择一个执行:
- 选择一个 2 × 2 2\times 2 2×2的矩阵,并将其变为白色。
- 选择任意一行,将其全变为白色。
问最少操作多少次,可以将整个 n × n n\times n n×n的矩阵全变为白色。
思路
-
容易发现,如果当前行 a i > 4 a_i > 4 ai>4,那么显然使用一次操作2最优。
-
如果相邻两行 a i ≤ 2 a_i \le 2 ai≤2,那么使用操作1最优,因为可能消除下一行。
-
如果当前的 a i ≤ 4 a_i\le 4 ai≤4,那么可能用操作1,也可能是操作2;此时若下一行 a i + 1 ≤ 4 a_{i+1}\le 4 ai+1≤4,那么考虑当前使用操作1。
综上,我们可以用 f i f_i fi表示前i行的最优解, g i g_i gi记录上述(3)的情况,然后进行状态转移即可。最后 f [ n ] f[n] 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); //默认都用操作2if(a[i] <= 2) { //这类情况可以判断是否用操作1g[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;
}