2A. Card Exchange (思维)
题意:
你有 n n n张牌,每张牌上都写着一个数字,还有一个固定整数 k k k 。你可以多次进行下面的运算:
- 从手牌中选择任意 k k k 张数字相同的牌。
- 将这些牌换成 k − 1 k-1 k−1 张牌,每张牌上可以换成任何数字。
在这个过程结束时,你手中最少有多少张牌?
分析:
如果存在某种数字的数量超过 k k k就可以一直进行操作,直到卡片数量为 k − 1 k-1 k−1,否则不能替换。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
vector<int> a(n + 1, 0);
map<int, int> mp;
int flag = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
mp[a[i]]++;
if (mp[a[i]] >= k)
flag = 1;
}
if (flag)
cout << k - 1 << endl;
else
cout << n << endl;
}
return 0;
}
2B.Rectangle Filling (思维)
题意:
有一个由白色和黑色方格组成的
n
×
m
n \times m
n×m 网格。在一次操作中,可以选择任意两个相同颜色的方格,并将它们之间的子方格中的所有方格都染成相同的颜色。
询问在进行任意次数可能为零的运算后,网格中所有网格的颜色是否可能相同?
分析:
只需要四条边缘上都有白色或者黑色色块,就可以使得所有网格的颜色相同。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
vector<string> s(n + 5);
for (int i = 1; i <= n; i++)
cin >> s[i];
if (s[1][0] == s[n][m - 1] || s[1][m - 1] == s[n][0]) {
cout << "YES" << endl;
continue;
}
int flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0;
for (int i = 0; i < m; i++)
if (s[1][i] == 'W')
flag1 = 1;
for (int i = 0; i < m; i++)
if (s[n][i] == 'W')
flag2 = 1;
for (int i = 1; i <= n; i++)
if (s[i][0] == 'W')
flag3 = 1;
for (int i = 1; i <= n; i++)
if (s[i][m - 1] == 'W')
flag4 = 1;
if (flag1 && flag2 && flag3 && flag4) {
cout << "YES" << endl;
continue;
}
flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0;
for (int i = 0; i < m; i++)
if (s[1][i] == 'B')
flag1 = 1;
for (int i = 0; i < m; i++)
if (s[n][i] == 'B')
flag2 = 1;
for (int i = 1; i <= n; i++)
if (s[i][0] == 'B')
flag3 = 1;
for (int i = 1; i <= n; i++)
if (s[i][m - 1] == 'B')
flag4 = 1;
if (flag1 && flag2 && flag3 && flag4) {
cout << "YES" << endl;
continue;
} else
cout << "NO" << endl;
}
return 0;
}
2C1A.Everything Nim (博弈论)
题意:
爱丽丝和鲍勃正在玩一个关于
n
n
n 堆石子的游戏。轮到每个玩家时,他们都会选择一个正整数
k
k
k ,这个正整数最多等于最小的非空堆的大小,然后从每个非空堆中一次性取出
k
k
k 个石子。第一个没有石子可取的人输。
保证爱丽丝先下,并且如果双方都以最佳方式下棋,判断谁会赢。
分析:
假设最少的堆中只有一个石子,那么只能选择取一个,否则就可以自由决定取完最少堆或取到只剩一个来控制先后手。所以在我们轮流将只能取一个的堆取完后,谁先手则必胜,并且还需要额外考虑只能一直轮流取的情况。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
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());
a.erase(unique(a.begin(), a.end()), a.end());
int num = a.size() - 1;
int tmp = -1;
for (int i = 1; i <= num; i++)
if (a[i] != i) {
tmp = i;
break;
}
if (tmp == -1) {
if (num % 2)
cout << "Alice" << endl;
else
cout << "Bob" << endl;
continue;
}
if (tmp % 2 == 0)
cout << "Bob" << endl;
else
cout << "Alice" << endl;
}
return 0;
}
2D1B.Missing Subsequence Sum (构造)
题意:
构造一个长度最大为 25 25 25的数组,使得数组的子序列和包含从 1 − n 1-n 1−n除了 k k k以外所有数。
分析:
如果要去掉 k k k,那么需要找出 k k k二进制表示最大的那一位并将其去掉,再补齐其他数。设 k k k的最大位为 2 2 2的 i i i次方,则加上 k − 2 i k-2^i k−2i 就可以满足 1 1 1 到 k − 1 k-1 k−1 的数,构造大于 k k k 的数需要加上 k + 1 k+1 k+1,后面构造的数字都可以在加上 k + 1 k+1 k+1的基础上构造,于是再添加 2 i + k + 1 2^i + k +1 2i+k+1与 k + 1 k+1 k+1 的差就可以实现剩下的数字。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
vector<int> ans;
cin >> n >> k;
int i = 1, rest = 0;
while (1) {
if (k == 1)
break;
if (rest + i + 1 >= k) {
ans.push_back(k - rest - 1);
break;
} else {
ans.push_back(i);
rest += i;
}
i <<= 1;
}
ans.push_back(k + 1), ans.push_back(k * 2 + 1);
int tmp = k * 3;
for (int i = k * 2; tmp < n; i <<= 1)
ans.push_back(tmp - k), tmp += i;
cout << ans.size() << endl;
for (auto v: ans) {
cout << v << " ";
}
cout << endl;
}
return 0;
}
2E1C. Folding Strip (思维)
题意:
有一张纸条,上面是长度为
n
n
n 的二进制字符串
s
s
s 。现在可以将纸条折叠在任意一对相邻数字之间。
如果在折叠后,位于上方或下方的所有字符都匹配,则认为一组折叠有效。请注意,所有折叠都是同时进行的,因此折叠之间的字符不必匹配。
在完成一组有效的折叠后,能形成的最小长度条带是多少?
分析:
考虑到折叠后的纸条必然是交替的串,那么相邻位置如果相同的话一定会被折叠。那么就是先顺着一个方向折叠,遇到相同的数字就转向,记录一下往左往右能折叠到最远的地方,这个距离就是答案。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int mod = 998244353;
int a[N];
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
string tmp;
cin >> tmp;
for (int i = 0; i < n; i++) {
a[i + 1] = tmp[i] - '0';
}
int l = 1, r = 1, now = 1, w = 1;
a[0] = -1;
for (int i = 1; i <= n; i++) {
if (a[i] == a[i - 1])
w *= -1;
now += w;
l = min(l, now);
r = max(r, now);
}
cout << r - l << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。