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];
    }
}
  
 
                
            
        
浙公网安备 33010602011771号