Codeforces Round 1005 (Div. 2)

A. Brogramming Contest

题目大意

给你一个01串s,每次你可以从s移动部分后缀给t(初始为空),也可以从t移动部分后缀给s,问让s全是0,t全是1的最小操作次数

解题思路

显然不相等的部分就要分离,因此只需统计有多少次01变化即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, ans = 0, now = 0;
        std::cin >> n;

        std::string s;
        std::cin >> s;

        for (int i = 0; i < n; i++) {
            if (s[i] - '0' != now) {
                ans++;
                now = s[i] - '0';
            }
        }

        std::cout << ans << "\n";
    }
}

B. Variety is Discouraged

题目大意

给你一个数组a,定义得分为a的长度减去a中不同的元素数量,可以删除一次子串,要求给出任意一个最大得分时的最短数组

解题思路

简单观察可以发现,只出现1次的元素不会贡献得分,反而会增加长度,显然这是我们要剔除的部分,所以只需要找到唯一元素的最长连续子数组即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n;
        std::cin >> n;

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

        for (int i = 1; i <= n; i++) {
            if (cnt[a[i]] == 1) {
                f[i] = 1;
            }
        }

        int Len = 0, l = -1, r = -1, i = 1;
        while (i <= n) {
            if (!f[i]) {
                i++;
                continue;
            }
            int j = i;
            while (j <= n && f[j]) {
                j++;
            }
            int len = j - i;
            if (len > Len) {
                Len = len;
                l = i;
                r = j - 1;
            }
            i = j;
        }

        if (Len > 0) {
            std::cout << l << " " << r << "\n";
        } else {
            std::cout << 0 << "\n";
        }
    }
}

C. Remove the Ends

题目大意

给你一个数组,ai小于0时你可以减去ai然后删除ai及以后的部分,否则加上ai然后删除ai及之前的部分,问最大的和是多少

解题思路

负数始终保留前一部分,非负数始终保留后一部分,简单分析可知同符号的数字可以一直被选择,并且异号选择的数字是不会交错的(就是答案数组的左右两部分一定是一边负一边非负),因此只需要对前后缀数组扫描一次即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        i64 n, ans = 0;
        std::cin >> n;

        std::vector<i64> a(n + 1), pre(n + 1), suff(n + 2);
        for (int i = 1; i <= n; i++) {
            std::cin >> a[i];
            pre[i] = pre[i - 1];
            if (a[i] >= 0) {
                pre[i] += a[i];
            }
        }
        for (int i = n; i >= 1; i--) {
            suff[i] = suff[i + 1];
            if (a[i] < 0) {
                suff[i] -= a[i];
            }
        }

        for (int i = 1; i <= n; i++) {
            ans = std::max(ans, pre[i] + suff[i]);
        }

        std::cout << ans << "\n";
    }
}

D. Eating

题目大意

给你一个数组,每次询问在初始数组后面加入一个数字,如果倒数第一个数字大于倒数第二个数字,则可以吞噬倒数第二个数字,并把倒数第一个数字更新为二者的异或和,问每次能吞噬初始数组多少个数字

解题思路

逐步模拟显然会超时,涉及二进制要比大小首先看最高位,最高位一定是单调不递增的,如果最后两个数字最高位相同,那么异或后就会变小,由于最高位最多只会变化30次,所以可以从这里考虑,我们要找的其实是当前范围内最右侧的和最后一个数字最高位相同的那个值的位置,这样能跳过中间部分快速更新最后一个位置,可以预处理二进制数最高位的信息,然后再用异或前缀和快速更新最后的值

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, q;
        std::cin >> n >> q;

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

        std::vector<std::array<int, 30>> left(n);
        left[0].fill(-1);
        for (int i = 0; i < n; i++) {
            if (i) {
                left[i] = left[i - 1];
            }
            int d = std::__lg(a[i]);
            for (int bit = 0; bit <= d; bit++) {  // 0~i范围内最右侧二进制最高位不小于bit位的位置,后续快速查询用
                left[i][bit] = i;
            }
        }

        auto pre = a;
        for (int i = 1; i < n; i++) {  // 异或前缀和可以快速更新
            pre[i] ^= pre[i - 1];
        }

        while (q--) {
            int x;
            std::cin >> x;

            int now = n - 1;
            while (x > 0 && now >= 0) {
                int bit = std::__lg(x);
                // 找到左边遇到的第一个能达到x最高位的位置,这个位置可能的值可能会改变x的最高位,或者比x大会让让吞噬过程中断
                x ^= pre[now] ^ pre[left[now][bit]];  
                now = left[now][bit];
                if (now == -1 || x < a[now]) {  // -1说明已经全部吞噬了,小于则无法继续吞噬
                    break;
                }
                x ^= a[now];
                now--;
            }
            std::cout << (n - 1 - now) << " \n"[q == 0];
        }
    }
}

E. Mycraft Sand Sort

题目大意

给你两个数组p(p是排列)和c,表示在二维的有重力的环境下有 pici 颜色的方块(它们会往下掉到地面,不会悬空),问有多少对(p',c')使得掉落结果和(p,c)相同

解题思路

第一列始终是不会掉落的,所以c就是c',只用考虑p'即可。可以发现如果同色相邻,则能够直接交换;如果不相邻,但是同色的数量都多于二者中间那些颜色也是可以交换的。因此可以按照值从小到大来做,并查集维护可以交换的连通块,对于当前这行来说,它就是同颜色里数量最少的,所以两边的颜色如果相同就可以合并,然后删除它(同色合并一定轮不到它),交换的方案数乘上当前颜色的数量即可,记得每次处理完都要把连通块大小减1,避免重复

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int MOD = 998244353;

class DSU {
   public:
    int cnt;
    std::vector<int> fa, rank, siz;

    DSU(int n) : cnt(n), fa(n + 1), rank(n + 1), siz(n + 1, 1) {
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
        }
    }

    int find(int x) {
        if (fa[x] != x) {
            fa[x] = find(fa[x]);
        }
        return fa[x];
    }

    void merge(int x, int y) {
        int X = find(x), Y = find(y);
        if (X != Y) {
            if (rank[X] >= rank[Y]) {
                fa[Y] = X;
                siz[X] += siz[Y];
                if (rank[X] == rank[Y]) {
                    rank[X]++;
                }
            } else {
                fa[X] = Y;
                siz[Y] += siz[X];
            }
            cnt--;
        }
    }

    int size() {
        return cnt;
    }

    int count(int x) {
        return siz[find(x)];
    }
};

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n;
        std::cin >> n;

        std::vector<int> p(n + 1), c(n + 1), l(n + 1), r(n + 1);
        for (int i = 1; i <= n; i++) {
            int x;
            std::cin >> x;
            p[x] = i;
        }
        for (int i = 1; i <= n; i++) {
            std::cin >> c[i];
            l[i] = i - 1;
            r[i] = i + 1;
        }

        DSU dsu(n);
        for (int i = 1; i < n; i++) {
            if (c[i] == c[i + 1]) {
                dsu.merge(i, i + 1);
            }
        }

        i64 ans = 1;
        for (int i = 1; i <= n; i++) {
            int fa = dsu.find(p[i]);
            ans = ans * dsu.siz[fa] % MOD;
            dsu.siz[fa]--;

            int L = l[p[i]], R = r[p[i]];
            if (L >= 1 && R <= n && c[L] == c[R]) {
                dsu.merge(L, R);
            }

            if (L >= 1) {
                r[L] = R;
            }
            if (R <= n) {
                l[R] = L;
            }
        }

        std::cout << ans << "\n";
    }
}
posted @   udiandianis  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
  1. 1 山海不可平 CMJ
山海不可平 - CMJ
00:00 / 00:00
An audio error has occurred.

纯音乐,请欣赏

点击右上角即可分享
微信分享提示