Codeforces Round #767 (Div. 2)题解
传送门:Codeforces Round #767 (Div. 2)
A. Download More RAM
题意:商店有n n n 个拓展内存的软件,第i i i 个软件需要使用a i a_i a i 的内存,并给手机增加b i b_i b i 的内存。问手机最多能获得多少内存。
思路:贪心,直接按照a a a 从小到大排序,如果当前内存足够a a a 就将内存+ b +b + b ,否则退出循环。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #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; vector<int > a (n) , b (n) ; for (auto & v : a) { cin >> v; } for (auto & v : b) { cin >> v; } vector<int > p (n) ; iota (p.begin (), p.end (), 0 ); sort (p.begin (), p.end (), [&] (int x, int y) { return a[x] < a[y]; }); int ans = k; for (auto & v : p) { if (ans >= a[v]) { ans += b[v]; } else { break ; } } cout << ans << "\n" ; } return 0 ; }
B. GCD Arrays
题意:一个数组包含从[ l , r ] [l,r] [ l , r ] 的所有数各一个。有一个操作,任选数组中的两个数将其删去并添加一个新数到数组,该新数为两个被删去的数的积。做多能操作k k k 次,最后能否使得数组的gcd > 1 \gcd \gt 1 g cd> 1 。
思路:为了让操作次数尽量少,我们可以让所有奇数和旁边的偶数相乘,最后让数组只留下偶数。我们只需要数出奇数个数并判断是否不超过k k k 即可。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <bits/stdc++.h> using namespace std;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int T; cin >> T; while (T--) { int l, r, k; cin >> l >> r >> k; if (l == r) { cout << (l == 1 ? "NO" : "YES" ) << "\n" ; continue ; } int odd = (r + 1 ) / 2 - l / 2 ; cout << (k >= odd ? "YES" : "NO" ) << "\n" ; } return 0 ; }
C. Meximum Array
题意:有一个数组,每次可以选择一个数k k k ,然后会将前k k k 个数的M E X MEX MEX 值添加到新数组的尾部,并将旧数组的前k k k 个数删除,直到数组为空。构造一个字典序最大的新数组。
思路:我们可以先把整个数组的M E X MEX MEX 求出来,此时的M E X MEX MEX 值一定是在所有k ∈ [ 1 , n ] k \in [1, n] k ∈ [ 1 , n ] 中的最大的。然后我们去遍历数组,假如遇到一个数加上后发现当前M E X MEX MEX 和最大的M E X MEX MEX 相同,我们直接将该数加入新数组,然后删去前k k k 个数。此时我们还需要去更新最大的M E X MEX MEX ,我们并不用重新去遍历,可以在边删数的时候边将M E X MEX MEX 更新,复杂度是线性的。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #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) , cnt (n + 1 ) ; for (auto & v : a) { cin >> v; cnt[v]++; } int mex = 0 ; for (int i = 0 ; i <= n; i++) { if (cnt[i] == 0 ) { mex = i; break ; } } vector<int > ans; vector<bool > ok (n + 1 ) ; int now = 0 , tmp = mex; for (auto & v : a) { ok[v] = true ; cnt[v]--; if (cnt[v] == 0 ) { tmp = min (tmp, v); } while (now <= n && ok[now]) { now++; } if (now == mex) { ans.push_back (now); for (int i = 0 ; i <= now; i++) { ok[i] = false ; } mex = min (mex, tmp); tmp = INT_MAX; now = 0 ; } } cout << int (ans.size ()) << "\n" ; for (auto & v : ans) { cout << v << " " ; } cout << "\n" ; } return 0 ; }
D. Peculiar Movie Preferences
题意:有一个字符串列表,每个字符串长度不超过3 3 3 ,需要在其中找到一个子序列使得它们所连成的字符串是一个回文串。
思路:分类讨论。
假如当前字符串的长度为1 1 1 ,那么一定正确;
假如当前字符串长度为2 2 2 ,如果两个字母本身就相同,一定正确;如果存在该字符串的取反的串,也一定正确;
假如当前字符串长度为3 3 3 ,如果第一个字母和最后一个字母想同,一定正确;如果存在该串取反的串,一定正确;如果在它前面存在后两个字母取反的串,一定正确,例如[ " c b " , . . . , " a b c " ] ["cb",...,"abc"] [ " c b " , ... , " ab c " ] ,这是可以组成的,但是[ " a b c " , . . . , " c b " ] ["abc",...,"cb"] [ " ab c " , ... , " c b " ] ,这就不能组成;如果在他后面最在前两个字母取反的串,一定正确,和刚刚的一样,顺序一定不能错。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #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; map<string, int > mp; bool ok = false ; int one = 0 ; for (int i = 0 ; i < n; i++) { string s; cin >> s; if (ok) continue ; mp[s]++; if (s.length () == 1 ) { ok = true ; continue ; } if (s.length () == 2 && s[0 ] == s[1 ]) { ok = true ; continue ; } if (s.length () == 3 && s[0 ] == s[2 ]) { ok = true ; continue ; } if (s.length () == 2 ) { string t = s; reverse (t.begin (), t.end ()); if (mp.count (t)) { ok = true ; continue ; } for (char ch = 'a' ; ch <= 'z' ; ch++) { t.push_back (ch); if (mp.count (t)) { ok = true ; } t.pop_back (); } if (ok) continue ; } if (s.length () == 3 ) { string t = s; reverse (t.begin (), t.end ()); if (mp.count (t)) { ok = true ; continue ; } t.pop_back (); if (mp.count (t)) { ok = true ; continue ; } } } cout << (ok ? "YES" : "NO" ) << "\n" ; } return 0 ; }
E. Grid Xor
题意:定义一个集合的异或和是集合内所有数异或的结果。现在知道一个二维数组,每个格子是它相邻的四个格子个异或和的结果,现在需要找到整个二维数组的异或和,该二维数组大小保证偶数。
思路:结论题。这题类似于开关灯问题,不过这题不会让自己改变状态,但道理相同。
我们将所有格子想象成按钮,按下后他四周的灯会改变状态,我们需要让所有灯变亮所需要操作的按钮,每个按钮最多被按下一次,因为再按下一次相当于没改变周围的状态。可以利用线性代数证明可行性,网上有很多证明过程。
由于这题保证有解,我们可以先让第一排为全暗的状态,从第二排开始,如果它上面的格子为暗的,就说明这是需要按下的按钮,然后同时要把另外几个相邻的灯状态改变。
其实这些需要操作的按钮所在的位置,就是整个数组异或和需要的数所在的位置,直接将这些数进行异或即可。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #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<vector<int >> a (n, vector<int >(n)); for (auto & v : a) { for (auto & vv : v) { cin >> vv; } } vector<vector<int >> b (n, vector<int >(n)); int ans = 0 ; for (int i = 1 ; i < n; i++) { for (int j = 0 ; j < n; j++) { if (b[i - 1 ][j] == 0 ) { ans ^= a[i][j]; b[i - 1 ][j] ^= 1 ; if (i + 1 < n) { b[i + 1 ][j] ^= 1 ; } if (j - 1 >= 0 ) { b[i][j - 1 ] ^= 1 ; } if (j + 1 < n) { b[i][j + 1 ] ^= 1 ; } } } } cout << ans << "\n" ; } return 0 ; }