2025.7.2

刷题日记
时隔一月,终于可以继续板刷cf了
前段时间一直在应付期末考试,身心俱疲



Codeforces Round 1029 (Div. 3) E. Lost Soul
https://codeforces.com/problemset/problem/2117/E
给定两个数组a和b,可以使用任意次的以下操作,要求让a[ i ] == b[ i ]的个数最大化

  1. Let a[ i ] = b[ i + 1 ]
  2. Let b[ i ] = a[ i + 1 ]
  3. Delete a[ i ] and b[ i ] (at most once)

经过观察可得,如果数组中存在以下三种情况,那么该位置以左的所有索引i都是可以转化的

  1. a[ i ] == a[ i + 1 ]
  2. b[ i ] == b[ i + 1 ]
  3. a[ i ] == b[ i ]

其实我们可以理解为只有第三种情况,因为第一种情况和第二种情况都是为了转化成第三种情况
那么我们其实可以注意到,不只是第一种和第二种情况可以
比如说在a数组中,如果两个相同的数间隔了偶数个数字,那么他们也是可以转化成第三种情况的
所以,我第一遍给出了一个比较暴力但保对的代码,如下所示:

点击查看代码
//====================Solution-bg====================//

void solve () {
    int n;
    std::cin >> n;

    std::vector<int> a(n + 1), b(n + 1);
    std::vector<int> ida[n + 1], idb[n + 1];

    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
        // ida[a[i]].push_back(i);
    }
    for (int i = 1; i <= n; i++) {
        std::cin >> b[i];
        // idb[b[i]].push_back(i);
    }
    
    if (a[n] == b[n]) {
        std::cout << n << '\n';
        return;
    }

    for (int i = 1; i <= n; i++) {
        ida[a[i]].push_back(i);
        idb[b[i]].push_back(i);
    }
    for (int i = 1; i <= n; i++) {
        std::sort(ida[i].begin(), ida[i].end(), [&] (int a, int b) { return a > b; });
        std::sort(idb[i].begin(), idb[i].end(), [&] (int a, int b) { return a > b; });
    }

    int res = -1;

    // without delete
    for (int i = n; i >= 1; i--) {
        for (auto j : ida[a[i]]) {
            if (i > j && (i - j) % 2 == 1) {
                res = std::max(res, j);
                break;
            }
        }

        if (idb[a[i]].empty()) continue;

        for (auto j : idb[a[i]]) {
            if (i >= j && (i - j) % 2 == 0) {
                res = std::max(res, j);
                break;
            }
        }
    }
    for (int i = n; i >= 1; i--) {
        for (auto j : idb[b[i]]) {
            if (i > j && (i - j) % 2 == 1) {
                res = std::max(res, j);
                break;
            }
        }

        if (ida[b[i]].empty()) continue;

        for (auto j : ida[b[i]]) {
            if (i >= j && (i - j) % 2 == 0) {
                res = std::max(res, j);
                break;
            }
        }
    }

    // delete
    for (int i = n; i >= 1; i--) {
        for (auto j : ida[a[i]]) {
            if (i > j && (i - j) % 2 == 0) {
                res = std::max(res, j);
                break;
            }
        }

        if (idb[a[i]].empty()) continue;

        for (auto j : idb[a[i]]) {
            if (i - j > 1 && (i - j) % 2 == 1) {
                res = std::max(res, j);
                break;
            }
        }
    }
    for (int i = n; i >= 1; i--) {
        for (auto j : idb[b[i]]) {
            if (i > j && (i - j) % 2 == 0) {
                res = std::max(res, j);
                break;
            }
        }

        if (ida[b[i]].empty()) continue;

        for (auto j : ida[b[i]]) {
            if (i - j > 1 && (i - j) % 2 == 1) {
                res = std::max(res, j);
                break;
            }
        }
    }
    
    if (res != -1)
        std::cout << res << '\n';
    else
        std::cout << 0 << '\n';
}

//====================Solution-ed====================//

交了一发,TLE on test3
后来一直想不出优化的方法,索性去看题解了
然后才意识到,其实非常简单:
既然你都可以删一列了,那么也就是说,你相同的数字之间,间隔奇数个还是偶数个都是可以转化的!
换句话讲,只要数组中出现相同的数,那么他们就是有用的,只需要取最优解就可以了!
由此,我们仅需一次遍历即可计算出答案
我们开一个seen数组标记某个数字是否出现过,在倒序遍历时出现的第一组相同数字,就是最终答案!
正确代码如下:

点击查看代码
//====================Solution-bg====================//

void solve () {
    int n;
    std::cin >> n;

    std::vector<int> a(n + 1), b(n + 1);
    std::vector<int> seen(n + 1, 0);

    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        std::cin >> b[i];
    }

    if (a[n] == b[n]) {
        std::cout << n << '\n';
        return;
    }

    // 由于题中所述,可以删除一列,故两相同数字索引之差为偶数为奇数时均可,故只要出现过就一定可以!!!!!!
    int ans = 0;
    for (int i = n - 1; i >= 1; i--) {
        if (a[i] == b[i] || a[i] == a[i + 1] || b[i] == b[i + 1] || seen[a[i]] || seen[b[i]]) {
            ans = i;
            break;
        }

        seen[a[i + 1]] = seen[b[i + 1]] = 1;
    }
    std::cout << ans << '\n';
}

//====================Solution-ed====================//


Educational Codeforces Round 180 (Rated for Div. 2) C. Coloring Game
https://codeforces.com/problemset/problem/2112/C

给定一个数组,现在Alice可从中任取三个数字染成红色,然后Bob从数组中任取一数字染成蓝色
(如果已经是红色,也可以改为蓝色),计算出Alice必胜的方案数
我们不难看出,Bob有两种决策:

  1. 将数组中的最大值染为蓝色
  2. 将已经染红的三个数字中的最大值染为蓝色

这两种决策是Bob的最优解

那么也就是说,对于Alice选的x,y,z三值,假设x <= y <= z,那么Bob要么选z,要么选a数组中的max
这样的话,前者Alice变成x + y,Bob变成z,等价于Alice还是x + y + z,Bob变成2 * z
后者Alice还是x + y + z,Bob变成max(a[ n ])
只要x + y + z > std::max(2 * z, a[ n ])
我们可以枚举每一种情况计算,但是这样会超时
我们可以只枚举y和z,然后用二分查找,在数组中查找有多少个x可以匹配y和z的组合,最后加到ans上即可
代码如下:

点击查看代码
//====================Solution-bg====================//

void solve () {
    int n;
    std::cin >> n;

    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }
    std::sort(a.begin() + 1, a.end());
    
    ll ans = 0;
    for (int z = 1; z <= n; z++) {
        for (int y = 1; y < z; y++) {
            int x = std::max(a[n], 2 * a[z]) - a[y] - a[z]; 
            int k = upper_bound(a.begin(), a.begin() + y, x) - (a.begin());
            ans += y - k;
        }
    }
    std::cout << ans << '\n';
}

//====================Solution-ed====================//
posted @ 2025-07-02 23:07  _彩云归  阅读(12)  评论(0)    收藏  举报