2025.7.18
刷题日记
菜
Teza Round 1 (Codeforces Round 1015, Div. 1 + Div. 2) D. Arcology On Permafrost
https://codeforces.com/contest/2084/problem/D
给定n, m, k三个数字,要求构造一个长度为n的数组
从中删除m个长度为k的连续子数组,剩下的数所构成的数组的MEX值最大化
代码和思路如下所示:
点击查看代码
//====================Solution-bg====================//
void solve () {
/*
猜想:
数组的MEX和m有关系
6th样例因为要删掉6个子数组,所以最大值不会很高
7th样例虽然删的很少,但最高也只能到5
因此可以看出,越想向上走,每一步的“花费”增加,并且增加很多
1. 假设删1个的数组
那么我们需要将n平分为2个重复数组
这样无论删那边儿,都有一边儿保持完整的递增,MEX不受影响
2. 假设删2个的数组
那么我们需要将n平分为3个重复数组
这样无论删那边儿,都有一边儿保持完整的递增,MEX不受影响
因此,我们认为,每个数应出现m + 1次
根据6th和7th样例,可以证明上述结论是正确的
目前看来,应该从上往下来,先保证最大的数字有m + 1个
或许子数组长度k,决定了每个相同的数应该相隔至少多远?
标答:
首先,f(a)最大为n - m * k,这是最理想状态
其次,为了保证“不断层”,f(a)应当为n / (m + 1),也就是每个数字必须至少出现m + 1次
因此,f(a)最大值 = std::min(n - m * k, n / (m + 1))
分为两种情况:
1. n - m * k < n / (m + 1) ==> n < (m + 1) * k ==> n - m * k < k
也就是a的最终长度是小于k的,此时只能令ans[i] = i % k
2. n - m * k >= n / (m + 1) ==> n >= (m + 1) * k ==> n / (m + 1) >= k
因此每个相同数字都能至少有k的距离,所以直接依次从0输出到n / (m + 1)即可
*/
int n, m, k;
std::cin >> n >> m >> k;
if (n < (m + 1) * k) {
for (int i = 0; i < n; i++) {
std::cout << i % k << " \n"[i == n - 1];
}
}
else {
for (int i = 0; i < n; i++) {
std::cout << i % (n / (m + 1)) << " \n"[i == n - 1];
}
}
/* 更简单的写法 */
// for (int i = 0; i < n; ++i) {
// std::cout << i % (n < (m + 1) * k ? k : n / (m + 1)) << " \n"[i == n - 1];
// }
}
//====================Solution-ed====================//
Codeforces Round 1032 (Div. 3) C. Those Who Are With Us
https://codeforces.com/contest/2121/problem/C
一场Div. 3,过了ABD,但C赛时WA5发一直没过,恼了
给定一个矩阵,要求选中一行一列,对其中的每个数字都 -1(交叉点也只-1)
求操作后矩阵中的最大数字的最小值
这道题也就两种情况:
- 选中的一行一列能覆盖矩阵中的所有最大值,此时答案为最大值-1
- 选中的一行一列无法覆盖矩阵中所有最大值,此时答案仍为最大值
我们可以开一个r数组和一个c数组,r[i]表示第i行有多少个最大值,c[i]记录第i列有多少个最大值
那么r[i] + c[i]就是选中某一行和某一列时,可以删除的最大值的数量
当然了,如果这一行的交叉点也是最大值,那就是r[i] + c[i] - 1
由此,我们遍历一遍矩阵中的每个点进行检查即可,代码如下
点击查看代码
//====================Solution-bg====================//
void solve () {
int n, m;
std::cin >> n >> m;
std::vector a(n + 1, std::vector<int>(m + 1));
std::vector<int> r(n + 1, 0), c(m + 1, 0);
int max = -inf, cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
std::cin >> a[i][j];
max = std::max(max, a[i][j]);
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == max) {
cnt++;
r[i]++;
c[j]++;
}
}
}
int flag = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == max) {
if (r[i] + c[j] - 1 == cnt) {
flag = 1;
}
} else {
if (r[i] + c[j] == cnt) {
flag = 1;
}
}
}
}
std::cout << max - flag << '\n';
}
//====================Solution-ed====================//
Codeforces Round 1032 (Div. 3) E. Sponsor of Your Problems
https://codeforces.com/contest/2121/problem/E
对于两个整数 a 和 b,将 f(a, b)定义为数字 a 和 b 的十进制表示中位数相同的位置数
例如,f(12, 21) == 0,f(31, 37) == 1,f(19891, 18981) == 2,f(54321, 24361) == 3
现在给定两个长度相同的十进制整数 l 和 r,对于任意整数 l <= x <= r,找出 f(l, x) + f(x, r)的最小值
其实这是一道字符串题
假设两个数字分别为122456和122478,那么我们显然可以看出,x的前几位也必须是1224,这样才满足l <= x <= r
这个时候必然有重复,因此int ptr = 0; while (l[ptr] == r[ptr] && ptr < l.size()) ptr++;
此时我们比较ptr位的后一位,如果相差了至少两个数字,那也就是后面都可以自由选了
比如47和58,只差一个数字,4和5必须二选一,后面要受限制
而47和68,只要选了5而非4、6,第二个数字可以随便选,
第二种情况好办,显然为if (l[ptr] + 1 < r[ptr]) std::cout << 2 * ptr << '\n';
接下来对第一种情况讨论:
二选一之后,后面必然受限制,但也不是很限制(
如果l的当前位为9,而r的当前位为0,此时一定不可避免地会重复
经证明可知只有该情况才会导致重复,而只要有一位不是这种情况,其后面的数位都不会重复
最终代码如下:
点击查看代码
//====================Solution-bg====================//
void solve () {
std::string l, r;
std::cin >> l >> r;
/* 只有一个数字可选 */
if (l == r) {
std::cout << 2 * l.size() << '\n';
return;
}
int ptr = 0;
while (l[ptr] == r[ptr] && ptr < l.size()) ptr++;
/* 如果满足这个条件,说明后面的范围大,数字可以随便选 */
if (l[ptr] + 1 < r[ptr]) {
std::cout << 2 * ptr << '\n';
return;
}
/* 否则,在这个位置上必须进行二选一的抉择,因为范围很小,一定会重复 */
int ans = 2 * ptr + 1;
for (int i = ptr + 1; i < l.size(); i++) {
/* 这种情况同样是没得选 */
if (l[i] == '9' && r[i] == '0') ans++;
else break;
}
std::cout << ans << '\n';
}
//====================Solution-ed====================//

浙公网安备 33010602011771号