Codeforces Round 1034(Div.3)[A-F]

A. Blackboard Game

题意

\(0\)\(n - 1\)的整数。两人轮流操作:

  • \(A\) 选择 一个整数 \(a\) 删除
  • \(B\) 选择一个整数 \(b\) 满足 \(a + b \equiv 3 (mod 4)\) 删除他

当一方无法操作时,另一方获胜。

tag: 博弈论

思路

可以看出,\(0,1,2,3\) 可以凑两对三,也就是不管 \(A\) 怎么选 \(B\) 都有的选,然后直到全部选完 \(A\) 落败。

\(n-1\)\(3\) 的倍数时 \(Bob\) 胜利

Code (C++)

void whx()
{
    int n;
    cin >> n;
    if(n % 4 != 0) cout << "Alice\n";
    else cout << "Bob\n";
}

B. Tournament

题意

给你一个长度为 \(n\) 的数组 \(a\)\(a_i\) 对应的是 \(i\) 的实力。

当剩下的人超过 \(k\) 时会进行淘汰。

淘汰规则:

  • 随机挑选两个人
  • 然后把两人中实力较弱的淘汰,若两人实力相等随机淘汰一个人

给你 \(j\)\(k\) ,判断第 \(j\) 个人有没有可能是最后剩下的 \(k\) 个人之一。

tag: 贪心,思维

思路

如果两个人值一样那么肯定优先留下要求的那个人。

那么我们可以记录下来 \(j\) 对应的值 \(v\)

可以发现大部分情况下想留下的人都能留下:

\(k > 1\) 的时候,只要死保想要的一个就一定行。

分析什么情况下留不下想留的人:

\(k = 1\) 的时候,除非 \(v\) 是最大值,能活到最后,不然留不下来。

Code (C++)

void whx()
{
    int n, j, k, target;
    cin >> n >> j >> k;
    int v = -1, ma = -1;
    for (int i = 1, a; i <= n; i ++ )
    {
        cin >> a;
        if (i == j) v = a;
        ma = max(ma, a);
    }
    if (v != ma && k == 1) cout << "NO\n";
    else cout << "YES\n";
}

C. Prefix Min and Suffix Max

题意

给你一个长度为 n 的数组 a, 在一次操作中,你可以:

  • 选择 a 的非空前缀,用他的最小值来替换。
  • 选择 b 的非空后缀, 用他的最大值来替换。

输出一个 01串 表示数组 a 对应位置的值能够通过若干次操作实现全覆盖。

tag: 前后缀数组

思路

预处理第 i 位前缀最小值,后缀最大值。

如果第 \(i\) 位的值和前缀最小值相等 或 和 后缀最大值相等,那么就可以全覆盖。

Code (C++)
、
void whx()
{
    int n;
    cin >> n;
    vector a(n + 1);
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    int pre_min = 1e6 + 7;
    vector suf_max(n + 2, 0);
    for (int i = n; i >= 1; i -- ) suf_max[i] = max(suf_max[i + 1], a[i]);
    string ans = "";
    for (int i = 1; i <= n; i ++ )
    {
        pre_min = min(pre_min, a[i]);
        if (pre_min == a[i] || a[i] == suf_max[i]) ans += '1';
        else ans += '0';
    }
    cout << ans << "\n";
}

D. Binary String Battle

题意

给你一个长度为 \(n\) 的二进制字符串 \(s\),和一个整数 \(k\) (\(1 \leq k < n\))。

如果 \(A\) 能把所有字符转化为零 \(A\) 就赢了。

\(A\)\(B\) 轮流下棋, \(A\) 先下:

  • \(A\) 可以选择一个长度为 \(k\) 的任意子序列,全部置零。
  • \(B\) 可以选择一格长度为 \(k\) 的任意字串,全部置零。

问谁能获胜。

tag: 博弈论

思路

\(1\) 个数的角度来分析:

如果 \(1\) 的个数 \(c1 \leq k\) ,那么 \(A\) 直接就可以获胜。

然后分析,\(A\) 每次操作可以让 \(1\) 减少 \(k\) 个,那什么是 \(A\) 的最优策略:

\(A\) 会努力 cut \(B\) 的恢复数,也就是尽量让 \(B\) 能选的字串中都包含 \(1\)

上述情况只有 \(k * 2 > n\) 的时候存在,也只有这种情况 \(A\)可以获胜。

Code (C++)

void whx()
{
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    int c1 = 0;
    for (int i = 0; i < n; i ++ ) c1 += (s[i] == '1');
    if (c1 <= k) cout << "Alice\n";
    else
    {
        if (k * 2 > n) cout << "Alice\n";
        else cout << "Bob\n";
    }
}

E. MEX Count

题意

给你一个大小为 \(n\) 的非负整数数组 \(a\)

对于所有的 \(k\) (\(0\leq k \leq n\)),计算移除k个值后,\(MEX(a)\) 可能的个数

tag: 差分

思路

MEX 的值域为 \([0, n]\)

统计每个值 \(v\) 的出现个数 \(cnt[v]\)

可以发现每个 \(v\) 会对于 不同的 \(k\) 产生一段连续的贡献。

遍历 \(v\)\(0\) 开始到 \(n\):

\(v\) 可以在 \(k \in [cnt[v], n - v]\) 中作为 MEX。可以用差分进行区间 \(+1\)

如果遇到了没有出现过的 \(v\) 那么跳出循环,因为后续不可能产生贡献了,后面 MEX 全是 前面这个每出现的了。

Code (C++)

void whx()
{
    int n;
    cin >> n;
    vector a(n + 1, 0);
    for (int i = 1, x; i <= n; i ++ )
    {
        cin >> x;
        a[x] ++;
    }
    vector ans(n + 2);
    for (int i = 0; i <= n; i ++ )
    {
        int k = a[i], t = n - i;
        ans[k] ++, ans[t + 1] --;
        if (!a[i]) break;
    }
    for (int i = 0; i <= n; i ++ )
    {
        if(i) ans[i] += ans[i - 1];
        cout << ans[i] << " \n"[i == n];
    }
}

F. Minimize Fixed Points

题意

输入一个 n ,构造长度为 n 的好排列 \(p\)

  • 对于 \(i > 1\),都有 \(gcd(p_i,i) > 1\) 记为好排列

  • 在长度为 n 的所有好排列中,找出一个固定点数最少的良好排列

  • 如果 \(p_i = i\) 记为一个固定点数

tag: 构造,质因数

思路

尽量让 \(i\) 的最大质因数的倍数来表示 \(p_i\)

1 2 3 4 5 6 7 8 9 10

​ 3 6 9

如上,可以发现把 3 6 9错位一下9 3 6就可以把三个位置全变成非固定点数

而且对于一个数能拆出来的大数一定比小数少,应该先把大数能表示的表示了。

比如 \(10\) 可以用 \(2\) 的倍数表示,可以用 \(5\) 的倍数表示,优先用 \(5\) ,然后你这里用了 \(2\) ,后面出现一个不是 \(5\) 的倍数但是 \(2\) 的倍数的数,你的 \(2\) 数量不够了。

预处理 \(n\) 内的所有质数,然后倒着遍历,错位赋给对应位置的 \(p\) 排列。

Code (C++)

void whx()
{
    int n;
    cin >> n;
    if (n <= 3)
    {
        for (int i = 1; i <= n; i ++ ) cout << i << " \n"[i == n];
    }
    else
    {
        vector a;
        auto is_prime = [&](int x) -> bool
        {
            if (x <= 2) return true;
            for (int i = 2; i <= x / i; i ++ )
            {
                if (x % i == 0) return false;
            }
            return true;
        };
        for (int i = n; i >= 2; i -- )
        {
            if (is_prime(i)) a.push_back(i);
        }
        vector ans(n + 1, 0);
        ans[1] = 1;
        for (auto p : a)
        {
            int last = 0;
            for (int i = p; i <= n; i += p)
            {
                if (ans[i]) continue;
                ans[i] = last;
                last = i;
            }
            ans[p] = last;
        }
        for (int i = 1; i <= n; i ++ ) cout << ans[i] << " \n"[i == n];
    }
}

posted @ 2025-07-04 18:41  __whx  阅读(77)  评论(0)    收藏  举报