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 匹配,所以我们只需交换 ST,让 S 变成较长串即可
然后计算 TS 的最长公共前缀和最长公共后缀,判定二者的累积和是否大于等于 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 坐标(记做 ymax),如果当前扫描到的是白色格子,那么如果它的 y 坐标  ymax 的话,就说明这个白色格子右下方有黑色格子,所以无解

代码实现
#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

注意到 (NK)106,这个限制条件保证了可以用爆搜来解决

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

代码实现
#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[1i]T[1j] 匹配的最小操作次数

时间复杂度为 O(NM)
但注意到当 |ij|>K 时的编辑距离一定大于 K,所以只需要考虑 |ij|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,MST 上的边权和等于 xx的边数,而 x的边数 又等于仅保留边权小于 x 的边所构成的连通块的个数 1
那么

GMST=Gx( 仅保留边权小于 x 的边所构成的连通块的个数 1)=xG( 仅保留边权小于 x 的边所构成的连通块的个数 1)
先不考虑 -1 的贡献
仅保留边权小于 x 的边所构成的连通块的个数等于 v1/( 包含点 v 的连通块的大小)

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

时间复杂度为 O(MN2)

代码实现
#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;
}