AtCoder Beginner Contest 212【A - E】
比赛链接:https://atcoder.jp/contests/abc212/tasks
A - Alloy
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int a, b;
cin >> a >> b;
if (a > 0 and b > 0) {
cout << "Alloy" << "\n";
} else if (a > 0) {
cout << "Gold" << "\n";
} else {
cout << "Silver" << "\n";
}
return 0;
}
B - Weak Password
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
cin >> s;
string t;
for (int i = 1; i < 4; i++) {
t += '0' + (s[i] - s[i - 1] + 10) % 10;
}
cout << (t == "000" or t == "111" ? "Weak" : "Strong") << "\n";
return 0;
}
C - Min Difference
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
set<int> a, b;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
a.insert(x);
}
for (int i = 0; i < m; i++) {
int x;
cin >> x;
b.insert(x);
}
int ans = INT_MAX;
for (auto i : a) {
auto it = b.lower_bound(i);
ans = min(ans, abs(*it - i));
if (it != b.begin()) {
ans = min(ans, abs(*prev(it) - i));
}
}
cout << ans << "\n";
return 0;
}
D - Querying Multiset
题解
考虑相对大小。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
cin >> q;
priority_queue<long long, vector<long long>, greater<long long>> pque;
long long delta = 0;
while (q--) {
int op;
cin >> op;
if (op == 1) {
int x;
cin >> x;
pque.push(x - delta);
} else if (op == 2) {
int x;
cin >> x;
delta += x;
} else {
cout << pque.top() + delta << "\n";
pque.pop();
}
}
return 0;
}
E - Safety Journey
题意
从一个含有 \(n\) 个结点的完全图中去掉 \(m\) 条边,问长为 \(k\) ,且起点和终点均为结点 \(1\) 的路径个数。
题解
由数据范围猜测应为二维 \(dp\) ,设 \(dp_{ij}\) 为从起点出发,长为 \(i\) ,终点为 \(j\) 的路径个数。
易得状态转移方程为:
vector<vector<int>> dp(k + 1, vector<int> (n));
dp[0][0] = 1; // 初始状态,长为 0 ,从结点 1 出发
for (int i = 0; i < k; i++) {
for (int u = 0; u < n; u++) { // 枚举下一层路径长度的终点
for (auto v : G[u]) { // 所有与终点相连的结点都可以转移到该点并使路径长度加一
dp[i + 1][u] += dp[i][v];
}
}
}
cout << dp[k][0] << "\n";
由于边界情况余下边数较多,内两层的循环次数多达 \(10^6\) ,所以不妨考虑不用加法,而是用减法得到下一层的结果。
类比一下就是:
a + b + c + d = e
ans = a + b + c // 为某一终点加上所有与之相邻结点的方案数
ans = e - d // 从总方案数中减去所有与该终点不相邻的结点的方案数
状态转移方程为:
vector<vector<int>> dp(k + 1, vector<int> (n));
dp[0][0] = 1;
for (int i = 0; i < k; i++) {
int sum = accumulate(dp[i].begin(), dp[i].end(), 0);
for (int u = 0; u < n; u++) {
dp[i + 1][u] = sum;
for (auto v : G[u]) {
dp[i + 1][u] -= dp[i][v];
}
}
}
cout << dp[k][0] << "\n";
最后可以压缩一下空间复杂度。
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int MOD = 998244353;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
vector<vector<int>> G(n);
for (int u = 0; u < n; u++) {
G[u].push_back(u);
}
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
--u, --v;
G[u].push_back(v);
G[v].push_back(u);
}
vector<int> dp(n);
dp[0] = 1;
for (int i = 0; i < k; i++) {
int sum = accumulate(dp.begin(), dp.end(), 0, [&](int x, int sum) { return (x + sum) % MOD; });
vector<int> next_dp(n);
for (int u = 0; u < n; u++) {
next_dp[u] = sum;
for (auto v : G[u]) {
next_dp[u] = (next_dp[u] - dp[v] + MOD) % MOD;
}
}
dp = next_dp;
}
cout << dp[0] << "\n";
return 0;
}