ARC168(A-C)题解

发布时间 2023-11-23 09:23:22作者: rong_nian

比赛链接:arc168

A

题意:

读入一个由<>构成的字符串,在最开始,最后,字符之间可以填上任意数字,任意两个相邻数字之间必须满足字符代表的大小关系。求问最后填入的数字组成的数组最少有多少对逆序对。

题解:

签到。

<可以不去考虑,因为不会对答案造成影响。

>如果不是在连续段内,也可以不去考虑,因为最优解必然是这部分贡献为 0 的。只需要考虑连续的 > 个数求解即可。

B

题意:

\(n\) 堆石子,Alice 先手,Bob后手,假设最多只能取走一堆非空石子中 \([1,k]\) 任意个数的石子。

如果不存在 \(k\) 导致 Alice 存在必胜策略,输出 0。

如果不存在最大的 \(k\) 使得 Alice 存在必胜策略,输出 -1。

输出最大的 \(k\)

题解:

最开始考虑很不全面,WA 了好几次

Nim 游戏扩展,回想 \([1,k]\) 这样的 Nim 游戏的必胜策略是什么,是:令 \(\text{SG}(x) = x \bmod (k+1)\),则 \(f=\text{SG}(a_1) \oplus \cdots \oplus \text{SG}(a_n) = 0\) 是必败情况。

考虑从上到下枚举 \(k\) 的暴力做法,当最初情况,即 \(k+1 \ge \max(a_i)\) 的时候,如果 \(f\) 已经非 0,那么输出 -1。

否则,当某一刻,\(f\) 不是 0 的时候,输出此时的 \(k\),如果到最后还没输出,那么答案就是 \(0\)

考虑加速这个过程,考虑到答案应该是,到输出之前,无论对于哪个 \(k\)\(f\) 都是 0,考虑 \(\bmod\) 操作只会影响较大的元素,那么考虑排完序后的 \(a_n\),根据模 \(k+1\) 后是否被影响,可以分为前 \(x\) 个元素和 后 \(n - x\) 个元素,如果前 \(x\) 个此时 \(f\) 不为 0,那么 \(k+1=a_{x+1}\) 会导致 \(a_{x+1}\) 变为 0,此时就可能对最后的 \(f\) 造成影响。如此这般循环考虑即可。

C

题意:

有一个长度为 \(N\) 的,仅由 ABC 三种字符组成的字符串。你可以进行 \([0,k]\) 次操作,每次操作是选择字符串中任意两个字符,求得到的字符串由多少种。

题解:

不会写这种东西,卡麻了

考虑假设知道一个目标字符串 \(T\) ,如何求由最开始知道的字符串 \(S\) 求得需要的最少操作次数。

\(C[ca][cb]\) 表示有多少个对应位置上 \(S_x = ca\)\(T_x = cb\)。每次操作可以使得 \(C[ca][cb]\)\(C[cb][ca]\) 分别减 1。

经过一定次数的操作后,必然可以得到某一些 \(C[ca][cb]\) 为 0,此时的考虑 \(C\) 数组有何关系。

手玩一下发现,\(C[A][B] = C[B][C] = C[C][A]\)\(C[B][A] = C[C][B] = C[A][C]\)。对于一对没配对的 ABC 可以通过 ABC -> ACB -> CAB 这样用 2 次操作,让三个对应的 \(C\) 分别减去 1。

考虑限制这个 \(C\) 数组在特定情况下分别计数。

发现需要考虑的有四个自由度:1. 前一种操作的三对 \((x,y)=(A,B),(B,C),(A,C)\) 操作分别的进行次数,2. 后一种操作的次数。

计数部分是一个较前面内容简单的组合数相乘,具体可以看代码。

(思路来源:ARC official Editorial

Code:

#include <bits/stdc++.h>
using namespace std;

using LL = long long;
using LF = double;
using pii = pair<int, int>;
#define fir first
#define sec second
#define mk make_pair

constexpr int N = 250005, M = 105, mod = 998244353;
int n, k;
LL fac[N], inv[N], cnt[5];
string s;
LL ans;

LL Cal(LL m, LL a) {
    if (m < a) return 0;
    return fac[m] * inv[m - a] % mod * inv[a] % mod;
}

void work() {
    cin >> n >> k >> s;
    for (auto c : s)
        cnt[c - 'A']++;
    for (int i = 0; 2 * i <= k; ++i)
        for (int a = 0; a + 2 * i <= k; ++a) // ab
            for (int b = 0; b + 2 * i + a <= k && i + a + b <= cnt[0]; ++b) // ac
                for (int c = 0; a + b + c + 2 * i <= k && i + a + c <= cnt[1] && i + b + c <= cnt[2]; ++c) { // bc
                    LL mans = 1, mas = 1;
                    mans = Cal(cnt[0], i + a + b) % mod * Cal(i + a + b, i + a) % mod * Cal(cnt[2], i + b + c) % mod * Cal(i + b + c, i + b) % mod * Cal(cnt[1], i + c + a) % mod * Cal(i + a + c, i + c) % mod;
                    mas *= Cal(cnt[0], i + a + b) % mod * Cal(i + a + b, i + b) % mod * Cal(cnt[2], i + b + c) % mod * Cal(i + b + c, i + c) % mod * Cal(cnt[1], i + c + a) % mod * Cal(i + a + c, i + a) % mod;
                    ans = (ans + mas + (i == 0 ? 0 : 1) * mans) % mod;
                }
    cout << ans << endl;
}

LL fpow(LL x, int b) {
    LL res = 1;
    while (b) {
        if (b & 1) res = res * x % mod;
        x = x * x % mod;
        b >>= 1;
    }
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    fac[0] = 1; n = 250000;
    for (int i = 1; i <= n; ++i) {
        fac[i] = fac[i - 1] * i % mod;
    }
    inv[n] = fpow(fac[n], mod - 2);
    for (int i = n - 1; ~i; --i) {
        inv[i] = inv[i + 1] * (i + 1) % mod;
    }
    int T = 1;
    while (T--) work();
    return 0;
}