A. Full House 2

只需判定 \(\{A, B, C, D\}\) 中是否只有 \(2\) 种数

代码实现
print('Yes' if len(set(map(int, input().split()))) == 2 else 'No')

B. Calculator

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    string s;
    cin >> s;
    int n = s.size();
    
    int ans = n;
    rep(i, n) {
        if (s.substr(i, 2) == "00") {
            ans--;
            s[i] = 'X';
            s[i+1] = 'X';
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

C. Operate 1

注意到对 \(S\) 删除一个字符使得它能和 \(T\) 匹配,等价于对 \(T\) 添加一个字符使得它能和 \(S\) 匹配,所以我们只需交换 \(S\)\(T\),让 \(S\) 变成较长串即可
然后计算 \(T\)\(S\) 的最长公共前缀和最长公共后缀,判定二者的累积和是否大于等于 \(T\) 的长度,再加上 \(S\) 的长度是否等于 \(T\) 的长度加 \(1\),就能得出是否能通过对 \(S\) 删掉一个字符得到 \(T\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

bool solve() {
    int k;
    string s, t;
    cin >> k >> s >> t;
    
    if (s == t) return true;
    if (s.size() == t.size()) {
        int cnt = 0;
        rep(i, s.size()) {
            if (s[i] != t[i]) cnt++;
        }
        return cnt <= 1;
    }
    
    if (s.size() < t.size()) swap(s, t);
    if (s.size() != t.size()+1) return false;
    
    int maxL = 0, maxR= 0;
    rep(i, t.size()) {
        if (s[i] != t[i]) break;
        maxL++;
    }
    for (int i = t.size()-1; i >= 0; --i) {
        if (s[i+1] != t[i]) break;
        maxR++;
    }
    return maxL+maxR >= t.size();
}

int main() {
    if (solve()) puts("Yes");
    else puts("No");
    
    return 0;
}

D. Diagonal Separation

容易发现如果有解的话,每个黑色格子左上方都是黑色格子,且每个白色格子右下方都是白色格子

可以考虑按 \(x\) 轴从后往前扫描,同时更新黑色格子的最大的 \(y\) 坐标(记做 \(y_{\max}\)),如果当前扫描到的是白色格子,那么如果它的 \(y\) 坐标 \(\leqslant \ y_{\max}\) 的话,就说明这个白色格子右下方有黑色格子,所以无解

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<tuple<int, int, char>> points;
    rep(i, m) {
        int x, y; char c;
        cin >> x >> y >> c;
        points.emplace_back(x, y, c);
    }
    
    ranges::sort(points, greater<>());
    
    int maxY = -1;
    for (auto [x, y, c] : points) {
        if (c == 'B') {
            maxY = max(maxY, y);
        }
        else {
            if (y <= maxY) {
                puts("No");
                return 0;
            }
        }
    }
    
    puts("Yes");
    
    return 0;
}

E. Maximize XOR

注意到 \(\binom{N}{K} \leqslant 10^6\),这个限制条件保证了可以用爆搜来解决

然后减减枝就行,当剩下要选的数的个数等于接下来没扫描过的数,那么意味着要把这些数全部选上才行,那么我们可以先预处理出序列的后缀异或和即可

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int n, k;
    cin >> n >> k;
    
    vector<ll> a(n);
    rep(i, n) cin >> a[i];
    
    vector<ll> s(n+1);
    for (int i = n-1; i >= 0; --i) s[i] = s[i+1]^a[i];
    
    ll ans = 0;
    auto dfs = [&](auto& f, int i, int k, ll x) -> void {
        if (k == 0) {
            ans = max(ans, x);
            return;
        }
        if (k == n-i) {
            ans = max(ans, x^s[i]);
            return;
        }
        f(f, i+1, k, x);
        f(f, i+1, k-1, x^a[i]);
    };
    dfs(dfs, 0, k, 0);
    
    cout << ans << '\n';
    
    return 0;
}

F. Operate K

其实就是编辑距离

dp[i][j] 表示使得 \(S[1 \sim i]\)\(T[1 \sim j]\) 匹配的最小操作次数

时间复杂度为 \(O(NM)\)
但注意到当 \(|i-j| > K\) 时的编辑距离一定大于 \(K\),所以只需要考虑 \(|i-j| \leqslant K\) 的情况。那么复杂度就变成了 \(O(NK)\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

inline void chmin(int& x, int y) { if (x > y) x = y; }

int main() {
    int k;
    string s, t;
    cin >> k >> s >> t;
    int n = s.size(), m = t.size();
    
    if (abs(n-m) > k) {
        puts("No");
        return 0;
    }
    
    const int INF = 1001001001;
    vector dp(n+1, vector<int>(k*2+1, INF));
    
    auto upd = [&](int i, int j, int x) {
        int e = j-i; // j = i+e
        if (e < -k or e > k) return;
        chmin(dp[i][e+k], x); 
    };
    
    upd(0, 0, 0); // i = j = 0
    rep(i, n+1) {
        for (int e = -k; e <= k; ++e) {
            int j = i+e;
            int now = dp[i][e+k];
            if (now == INF) continue;
            if (i < n) upd(i+1, j, now+1);
            if (j < m) upd(i, j+1, now+1);
            if (i < n and j < m) {
                upd(i+1, j+1, now+1);
                if (s[i] == t[j]) upd(i+1, j+1, now);
            }
        }
    }
    
    if (dp[n][(m-n)+k] <= k) puts("Yes");
    else puts("No");
    
    return 0;
}

G. Many MST

考虑 Kruskal,\(\operatorname{MST}\) 上的边权和等于 \(\sum\limits_x \geqslant x \text{的边数}\),而 \(\geqslant x \text{的边数}\) 又等于仅保留边权小于 \(x\) 的边所构成的连通块的个数 \(-1\)
那么

\(\sum\limits_G \operatorname{MST} = \sum\limits_G\sum\limits_x (\) 仅保留边权小于 \(x\) 的边所构成的连通块的个数 \(-1) = \sum\limits_x\sum\limits_G(\) 仅保留边权小于 \(x\) 的边所构成的连通块的个数 \(-1)\)
先不考虑 -1 的贡献
仅保留边权小于 \(x\) 的边所构成的连通块的个数等于 \(\sum\limits_v 1/(\) 包含点 \(v\) 的连通块的大小\()\)

\(f(x)\) 表示点 \(v\) 所在的连通块的大小为 \(x\) 的方案数
dp[i] 表示 \(i\) 个点的连通图有多少种

时间复杂度为 \(O(MN^2)\)

代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using mint = modint998244353;

int c2(int n) { return n*(n-1)/2; }

mint comb[505][505];

mint solve(int n, int m, int x) {
    vector<mint> pow_m(c2(n)+1, 1);
    rep(i, c2(n)) pow_m[i+1] = pow_m[i]*m;
    vector<mint> pow_x(c2(n)+1, 1);
    rep(i, c2(n)) pow_x[i+1] = pow_x[i]*x;
    
    vector<mint> dp(n+1);
    auto f = [&](int n, int k) { // 在n个点的图中恰有k个点连通的方案数
        mint res = dp[k];
        res *= pow_x[k*(n-k)];
        res *= pow_m[c2(n-k)];
        res *= comb[n-1][k-1];
        return res;
    };
    for (int i = 1; i <= n; ++i) {
        dp[i] = pow_m[c2(i)];
        for (int j = 1; j < i; ++j) dp[i] -= f(i, j);
    }
    mint res;
    for (int i = 1; i <= n; ++i) res += f(n, i)/i;
    res *= n;
    res -= pow_m[c2(n)];
    return res;
}

int main() {
    int n, m;
    cin >> n >> m;
    
    comb[0][0] = 1;
    rep(i, n)rep(j, i+1) {
        comb[i+1][j] += comb[i][j];
        comb[i+1][j+1] += comb[i][j];
    }
    
    mint ans;
    rep(x, m) ans += solve(n, m, m-x);
    
    cout << ans.val() << '\n';
    
    return 0;
}