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. 选中的一行一列能覆盖矩阵中的所有最大值,此时答案为最大值-1
  2. 选中的一行一列无法覆盖矩阵中所有最大值,此时答案仍为最大值
    我们可以开一个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====================//
posted @ 2025-07-18 22:15  _彩云归  阅读(37)  评论(0)    收藏  举报