CF2034 A-E题解

A. King Keykhosrow's Mystery

题意可以转化为存在 k1,k2 使得 m=a×k1+n=b×k2+n。消去余数 n 得到 a×k1=b×k2,即 a,b 的公倍数。所以最小的 m 就是 a,b 的最小公倍数,余数为 0。最小公倍数的计算方法是 lcm(a,b)=a×bgcd(a,b)

B. Rakhsh's Revival

考虑贪心的处理:逐位扫过去,维护一个变量 cnt 记录当前连续 0 的个数,遇到 1 就清空。因为提前操作并不能减少操作次数,所以我们仅当 cnt=m 时进行操作,把从这一位开始连续 k 个赋值成 1(实现时不用真的赋值,直接下标加 k 即可)。

int cnt = 0, ans = 0;
for (int i = 0; i < n; i++)
    if (s[i] == '1') { cnt = 0; continue; }
    else if (++cnt == m) i += k - 1, ans++, cnt = 0; // 因为for还要i++,这里只加k-1; 记得操作后cnt也要清零
cout << ans << '\n';

C. Trapped in the Witch's Labyrinth

对于 n×m 个格子,可以分 4 种情况考虑:

  • 连通的一片 ?:把连通的一片 ? 拆分成一个个矩形。除了 1×1 的矩形,都可以每行构造成 >>>>>< ,一定走不出去;1×1 的矩形直接指向别的 ? 即可。
  • 孤立的一个 ?:它的周围没有别的 ? 了,当且仅当存在确定方向的格子指向它时,它走不出去(反向指对方即可)。注意多个格子指向它时也不成问题,只需要反指任意一个,别的格子都会被导向这个 >< 的循环里。
  • 方向确定,一定能走出去:根据固定的方向最终走到了边界,或者已经确定的“能走出去的格子”。它们对答案没有贡献,dfs 时顺带标记掉就好。
  • 方向确定,走不出去:最终指向了 ?,走不出去。

所以从每个格子出发 dfs,如果是 ? 则把连通块的大小计入答案(大小为 1 即孤立时先不计入,插入 set 中以备查询);如果确定了方向则判断是否会走出,把走不出的连通块大小加入答案。最后对于所有的孤立点检查上下左右有没有走不出去的,有的话答案加 1

void solve(int test_case) {
    int n, m, siz, ans = 0;
    cin >> n >> m;
    vs s(n);
    vector<vc> vis(n, vc(m)); // 记录格子是否到达过
    vector<vc> out(n, vc(m)); // 记录格子是否会走出
    rep(i, 0, n - 1) cin >> s[i];
    set<pii> single; // 记录孤立点

    const int dx[4] = {-1, 1, 0, 0};
    const int dy[4] = {0, 0, -1, 1};
    map<char, int> mv = {{'U', 0}, {'D', 1}, {'L', 2}, {'R', 3}};

    auto inside = [&](int x, int y) -> bool {
        return x >= 0 && x < n && y >= 0 && y < m;
    };

    auto dfs1 = [&](auto&& dfs1, int x, int y) -> void { // 搜?的连通块
        vis[x][y] = 1;
        siz += 1;
        rep(i, 0, 3) {
            int tx = x + dx[i];
            int ty = y + dy[i];
            if (inside(tx, ty) && s[tx][ty] == '?' && !vis[tx][ty]) {
                dfs1(dfs1, tx, ty);
            }
        }
    };

    auto dfs2 = [&](auto&& dfs2, int x, int y) -> bool { // 搜方向确定的连通块
        vis[x][y] = 1;
        siz += 1;
        int d = mv[s[x][y]];
        int tx = x + dx[d];
        int ty = y + dy[d];
        if (!inside(tx, ty) || out[tx][ty]) {
            return out[x][y] = 1;
        }
        if (vis[tx][ty] || s[tx][ty] == '?') return 0;
        return out[x][y] = dfs2(dfs2, tx, ty);
    };

    rep(i, 0, n - 1) rep(j, 0, m - 1) {
        if (vis[i][j]) continue;
        siz = 0;
        if (s[i][j] == '?') {
            dfs1(dfs1, i, j);
            siz == 1 ? single.emplace(i, j), 0 : ans += siz;
        } else {
            ans += dfs2(dfs2, i, j) ? 0 : siz;
        }
    }
    for (auto [x, y] : single) {
        rep(i, 0, 3) {
            int tx = x + dx[i];
            int ty = y + dy[i];
            if (inside(tx, ty) && !out[tx][ty]) {
                ans += 1;
                break;
            }
        }
    }
    cout << ans << '\n';
}

D. Darius' Wisdom

用两个 set 记录 1,2 出现位置的下标,然后从后往前遍历,集合维护当前遍历位置前面的数(如果当前走到了 1,2 就从集合中删掉)。对于 0 来说,如果前面还有 2,就从集合中找到一个 1 进行交换,再找到一个 2 进行交换;前面没有 2 了,就找到 1 进行交换;1 都没有则退出。对于 1 来说,如果前面有 2 就进行交换,否则跳过。次数显然小于 n 次。

void solve(int test_case) {
    int n;
    cin >> n;
    vi a(n);
    vector<pii> ans;
    set<int> s1, s2;
    rep(i, 0, n - 1) {
        cin >> a[i];
        if (a[i] == 1) {
            s1.insert(i);
        } else if (a[i] == 2) {
            s2.insert(i);
        }
    }
    dep(i, n - 1, 0) {
        if (a[i] == 2) {
            s2.erase(i); // 当前的2遍历过了,删掉
            continue;
        }
        if (a[i] == 1) {
            if (s2.empty()) {
                s1.erase(i); // 当前的1遍历过了,删掉
                continue;
            }
            int k = *s2.begin();
            s2.erase(k);
            swap(a[i], a[k]); // 交换1,2
            s1.erase(i);
            s1.insert(k); // 更新1的位置
            ans.emplace_back(i, k);
            continue;
        } // 下面 a[i] == 0
        if (s2.empty()) {
            if (s1.empty()) break; // 都没有,交换结束
            int k = *s1.begin();
            s1.erase(k); // 没有2了,1的位置就确定了,直接删掉
            swap(a[i], a[k]);
            ans.emplace_back(k, i);
        } else {
            int k1 = *s1.begin();
            int k2 = *s2.begin();
            s2.erase(k2); // 2的位置确定了,可以删掉
            s1.erase(k1);
            a[i] = 2, a[k1] = 0, a[k2] = 1;
            s1.insert(k2); // 更新1的位置
            ans.emplace_back(i, k1);
            ans.emplace_back(i, k2);
        }
    }
    cout << ans.size() << '\n';
    for (auto [i, j] : ans) {
        write("%d %d\n", i + 1, j + 1);
    }
}

E. Permutations Harmony

首先要判定一下 kn!,这个只在 n 很小的情况下有可能超过,问题不大。然后注意到 k 为偶数时一定可以做,只需要找到 k2 个对称的排列即可,如 1234|4321, 2134|3421 等。k 为奇数时,需要进一步讨论:

  • k=1,只在 n=1 的情况下有解;k=n!1 一定无解。
  • n 为偶数时一定无解,因为每个 i 的和相同,总和为 n(n+1)k2,每个 i 的和为 (n+1)k2n 为偶数 (n+1)k 除不开。
  • n 为奇数时,k 可以拆成 3k3,后者为偶数,前者有贪心构造方案如图。

image

posted @   XYukari  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示