AtCoder Beginner Contest 421题解(ABCDFG)(赛时3题)
别问为什么没有E,不会动态规划TAT
A 请输入文本
判断第i个门的人是不是s即可
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
template<class T, class F> concept binary_func = convertible_to<F, function<bool(T, T)>>;
template<class T1, class T2, class F> requires(binary_func<T1, F> &&convertible_to<T2, T1>) bool chkf(T1 &x, const T2 &y, F &&f) {
if(f(static_cast<T1>(y), x)) {
x = static_cast<T1>(y);
return true;
}
return false;
}
template<class T1, class T2> bool chkmin(T1 &x, const T2 &y) {
return chkf(x, y, less<T1>{});
}
template<class T1, class T2> bool chkmax(T1 &x, const T2 &y) {
return chkf(x, y, greater<T1>{});
}
inline void solve() {
int n;
cin >> n;
vector<string> persons(n);
for(int i = 0; i < n; ++i) {
cin >> persons[i];
}
int idx; string dst;
cin >> idx >> dst;
--idx;
if(persons[idx] == dst) {
cout << "Yes\n";
} else {
cout << "No\n";
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n = 1;
// cin >> n;
for(int i = 0; i < n; ++i) {
solve();
}
return 0;
}
B 暴力
10很小,暴力草过去即可,注意开long long
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
template<class T, class F> concept binary_func = convertible_to<F, function<bool(T, T)>>;
template<class T1, class T2, class F> requires(binary_func<T1, F> &&convertible_to<T2, T1>) bool chkf(T1 &x, const T2 &y, F &&f) {
if(f(static_cast<T1>(y), x)) {
x = static_cast<T1>(y);
return true;
}
return false;
}
template<class T1, class T2> bool chkmin(T1 &x, const T2 &y) {
return chkf(x, y, less<T1>{});
}
template<class T1, class T2> bool chkmax(T1 &x, const T2 &y) {
return chkf(x, y, greater<T1>{});
}
inline void solve() {
ll a, b;
cin >> a >> b;
ll c;
for(int i = 3; i <= 10; ++i) {
string cs = to_string(a + b);
reverse(cs.begin(), cs.end());
c = stoll(cs);
a = b;
b = c;
}
cout << c << '\n';
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n = 1;
// cin >> n;
for(int i = 0; i < n; ++i) {
solve();
}
return 0;
}
C 数数题
没开long long但过于自信,直到D挂了才发现C也挂了,导致本来应该20分钟+一发罚时的这题变成52分钟了
考虑将所有A或者所有B放在下标为偶数的地方,然后容易证明将第i个A放在第i个偶数下标时最优(B同理),据此求解
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
template<class T, class F> concept binary_func = convertible_to<F, function<bool(T, T)>>;
template<class T1, class T2, class F> requires(binary_func<T1, F> &&convertible_to<T2, T1>) bool chkf(T1 &x, const T2 &y, F &&f) {
if(f(static_cast<T1>(y), x)) {
x = static_cast<T1>(y);
return true;
}
return false;
}
template<class T1, class T2> bool chkmin(T1 &x, const T2 &y) {
return chkf(x, y, less<T1>{});
}
template<class T1, class T2> bool chkmax(T1 &x, const T2 &y) {
return chkf(x, y, greater<T1>{});
}
inline void solve() {
int n;
cin >> n;
string s;
cin >> s;
ll op1 = 0, op2 = 0;
int even1 = 0, even2 = 0;
for(int i = 0; i < 2 * n; ++i) {
if(s[i] == 'A') {
op1 += abs(i - even1);
even1 += 2;
} else {
op2 += abs(i - even2);
even2 += 2;
}
}
cout << min(op1, op2) << '\n';
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n = 1;
// cin >> n;
for(int i = 0; i < n; ++i) {
solve();
}
return 0;
}
D 相对位置+计数
赛时写的代码不知道为什么不对,反正就是会WA,赛后让gemini改了过程中相遇的逻辑才过了(虽然除了相遇的逻辑以外还有一系列小错误,我糖丸了)
我们考虑将所有绝对位置改为相对位置,然后将路程分段(至多 \(m+l\) 段),对于每一段,如果起点为0且相对位移为0,则答案加上这一段的长度;如果终点为0答案+1,如果均不,考虑尝试寻找路程中间是否会有相遇的存在,存在则+1
// part that has comment are generated by Gemini 2.5 Pro at 2025.8.30
#include "bits/stdc++.h"
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using cd = complex<ld>;
template<class T, class F> concept binary_func = convertible_to<F, function<bool(T, T)>>;
template<class T1, class T2, class F> requires(binary_func<T1, F> &&convertible_to<T2, T1>) bool chkf(T1 &x, const T2 &y, F &&f) {
if(f(static_cast<T1>(y), x)) {
x = static_cast<T1>(y);
return true;
}
return false;
}
template<class T1, class T2> bool chkmin(T1 &x, const T2 &y) {
return chkf(x, y, less<T1>{});
}
template<class T1, class T2> bool chkmax(T1 &x, const T2 &y) {
return chkf(x, y, greater<T1>{});
}
ll sign(ll x) {
return x > 0 ? 1 : x < 0 ? -1 : 0;
}
inline void solve() {
ll xt, yt, xa, ya;
cin >> xt >> yt >> xa >> ya;
ll dx = xt - xa, dy = yt - ya;
ll n, m, l;
cin >> n >> m >> l;
vector<pair<ll, ll>> movet(m), movea(l);
for(int i = 0; i < m; ++i) {
char pos;
ll del;
cin >> pos >> del;
if(pos == 'U') {
movet[i].first = -del;
} else if(pos == 'D') {
movet[i].first = del;
} else if(pos == 'L') {
movet[i].second = -del;
} else {
movet[i].second = del;
}
}
for(int i = 0; i < l; ++i) {
char pos;
ll del;
cin >> pos >> del;
if(pos == 'U') {
movea[i].first = -del;
} else if(pos == 'D') {
movea[i].first = del;
} else if(pos == 'L') {
movea[i].second = -del;
} else {
movea[i].second = del;
}
}
ll ans = 0;
for(int it = 0, ia = 0; it < m;) {
// 在 for 循环内,计算 delta 之前
// 高桥当前一步的移动
ll dtx = sign(movet[it].first);
ll dty = sign(movet[it].second);
// 青木当前一步的移动
ll dax = sign(movea[ia].first);
ll day = sign(movea[ia].second);
// 相对速度
ll vx = dtx - dax;
ll vy = dty - day;
ll delta = min(abs(movet[it].first) + abs(movet[it].second), abs(movea[ia].first) + abs(movea[ia].second));
// 新的相遇判断逻辑
if(vx == 0 && vy == 0) {
if(dx == 0 && dy == 0) {
ans += delta;
}
} else {
// 求解 k * vx = -dx 和 k * vy = -dy
// t 是我们要求的相遇时间 k
ll t = -1;
if(vx != 0) {
// x 轴上需要 t 时间相遇
if(dx % vx == 0) {
t = -dx / vx;
}
}
if(vy != 0) {
// y 轴上需要 t_y 时间相遇
if(dy % vy == 0) {
ll t_y = -dy / vy;
if(t != -1 && t != t_y) { // 如果 x, y 轴算出的时间不同
t = -1; // 矛盾,不可能相遇
} else if(t == -1) { // 如果 x 轴方向相对静止
t = t_y;
}
} else {
t = -1; // y 轴不能在整数时间相遇
}
}
// 检查 t 是否是一个在 [1, delta] 范围内的解
if(t > 0 && t <= delta) {
// 还需要确保在 t 时刻,两轴都能相遇
if(dx + t * vx == 0 && dy + t * vy == 0) {
ans++;
}
}
}
// 更新 dx, dy 到这个时间段的末尾
dx += delta * vx;
dy += delta * vy;
if(movet[it].first > 0) {
if(movet[it].first > delta) {
movet[it].first -= delta;
} else {
++it;
}
} else if(movet[it].first < 0) {
if((-movet[it].first) > delta) {
movet[it].first += delta;
} else {
++it;
}
} else if(movet[it].second > 0) {
if(movet[it].second > delta) {
movet[it].second -= delta;
} else {
++it;
}
} else {
if((-movet[it].second) > delta) {
movet[it].second += delta;
} else {
++it;
}
}
if(movea[ia].first > 0) {
if(movea[ia].first > delta) {
movea[ia].first -= delta;
} else {
++ia;
}
} else if(movea[ia].first < 0) {
if((-movea[ia].first) > delta) {
movea[ia].first += delta;
} else {
++ia;
}
} else if(movea[ia].second > 0) {
if(movea[ia].second > delta) {
movea[ia].second -= delta;
} else {
++ia;
}
} else {
if((-movea[ia].second) > delta) {
movea[ia].second += delta;
} else {
++ia;
}
}
}
cout << ans << '\n';
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n = 1;
// cin >> n;
for(int i = 0; i < n; ++i) {
solve();
}
return 0;
}
F 链表
按照题意,操作为1时插入,操作为2时删除,使用链表模拟即可,因为总共只有 \(q\) 个节点,故最多删 \(q\) 个节点,复杂度 \(O(q)\)
我不知道这玩意为什么放在D和E后面,难道出题人觉得不会相对位置和不会动态规划的人绝对不会这么简单的链表吗,这玩意放力扣上都只有中等题
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
template<class T, class F> concept binary_func = convertible_to<F, function<bool(T, T)>>;
template<class T1, class T2, class F> requires(binary_func<T1, F> &&convertible_to<T2, T1>) bool chkf(T1 &x, const T2 &y, F &&f) {
if(f(static_cast<T1>(y), x)) {
x = static_cast<T1>(y);
return true;
}
return false;
}
template<class T1, class T2> bool chkmin(T1 &x, const T2 &y) {
return chkf(x, y, less<T1>{});
}
template<class T1, class T2> bool chkmax(T1 &x, const T2 &y) {
return chkf(x, y, greater<T1>{});
}
inline void solve() {
int q;
cin >> q;
vector<int> lst(q + 1, -1);
for(int i = 1; i <= q; ++i) {
int op;
cin >> op;
if(op == 1) {
int x;
cin >> x;
lst[i] = lst[x];
lst[x] = i;
} else {
int x, y;
cin >> x >> y;
int xx = x, yy = y;
ll sumx = 0, sumy = 0;
bool flagx = true, flagy = true;
while(true) {
if(lst[x] != -1) {
if(lst[x] == yy) {
cout << sumx << '\n';
lst[xx] = yy;
break;
} else {
x = lst[x];
sumx += x;
}
}
if(lst[y] != -1) {
if(lst[y] == xx) {
cout << sumy << '\n';
lst[yy] = xx;
break;
} else {
y = lst[y];
sumy += y;
}
}
}
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n = 1;
// cin >> n;
for(int i = 0; i < n; ++i) {
solve();
}
return 0;
}
G 费用流经典应用
不减->差分数组均大于0-(最小操作次数)->费用流
\([l, r)\) 区间加等价于差分数组在 \(l\) 处加一,在 \(r\) 处减一,故:
- 差分数组大于0的地方,可以为其他点提供贡献,故超级源点给它一个流量为 \(d[i]\) ,费用为0的路
- 差分数组小于0的地方,需要从其他点得到贡献,故向超级汇点一个流量为 \(-d[i]\) ,费用为0的路
- 数组首尾可以为其他节点提供无穷多的贡献,故超级源点给它们流量为 \(\infty\) ,费用为0的路
- 每一个区间( \([l, r)\) )等价于 \(r\) 所在的点向 \(l\) 所在的点提供一个流量为 \(\infty\) ,费用为1的路
最后跑最小费用最大流,如果能流满说明所有需求均能满足,此时输出费用;否则需求不能满足,输出-1
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
inline constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
template<class T, class F> concept binary_func = convertible_to<F, function<bool(T, T)>>;
template<class T1, class T2, class F> requires(binary_func<T1, F> &&convertible_to<T2, T1>) bool chkf(T1 &x, const T2 &y, F &&f) {
if(f(static_cast<T1>(y), x)) {
x = static_cast<T1>(y);
return true;
}
return false;
}
template<class T1, class T2> bool chkmin(T1 &x, const T2 &y) {
return chkf(x, y, less<T1>{});
}
template<class T1, class T2> bool chkmax(T1 &x, const T2 &y) {
return chkf(x, y, greater<T1>{});
}
struct EK {
struct Edge {
int dst, rev;
ll weight, flow;
bool neg;
};
vector<vector<Edge>> graph;
vector<int> pv, pe;
vector<ll> mf, d;
vector<bool> inq;
int n;
explicit EK(int _n) : n(_n), graph(_n), pv(_n, -1), pe(_n, -1), d(_n, inf), mf(_n, 0), inq(_n, false) {}
void addedge(int u, int v, ll flow, ll weight) {
int iu = graph[u].size(), iv = graph[v].size();
graph[u].push_back({ v, iv, weight, flow, false });
graph[v].push_back({ u, iu, -weight, 0, true });
}
pair<ll, ll> solve() {
ll cost = 0, flow = 0;
while(true) {
for(int i = 0; i < n; ++i) {
d[i] = inf;
mf[i] = 0;
inq[i] = false;
pv[i] = -1;
pe[i] = -1;
}
queue<int> q;
q.push(0);
d[0] = 0;
mf[0] = inf;
inq[0] = true;
while(!q.empty()) {
int u = q.front();
q.pop();
inq[u] = false;
for(int i = 0; i < graph[u].size(); ++i) {
auto &e = graph[u][i];
int v = e.dst;
ll f = e.flow, w = e.weight;
if(f > 0 && chkmin(d[v], d[u] + w)) {
pv[v] = u;
pe[v] = i;
mf[v] = min(mf[u], f);
if(!inq[v]) {
q.push(v);
inq[v] = true;
}
}
}
}
if(mf[n - 1] <= 0) break;
ll add = mf[n - 1];
for(int v = n - 1; v != 0; v = pv[v]) {
int u = pv[v], e = pe[v];
graph[u][e].flow -= add;
graph[v][graph[u][e].rev].flow += add;
}
flow += add;
cost += add * d[n - 1];
}
return { cost, flow };
}
};
inline void solve() {
int n, m;
cin >> n >> m;
vector<int> nums(n);
for(int i = 0; i < n; ++i) {
cin >> nums[i];
}
vector<int> diff(n + 1);
for(int i = 1; i < n; ++i) {
diff[i] = nums[i] - nums[i - 1];
}
diff[0] = diff[n] = 0x3f3f3f3f;
vector<pair<int, int>> rngs(m);
for(int i = 0; i < m; ++i) {
cin >> rngs[i].first >> rngs[i].second;
}
EK graph(n + 5);
const int src = 0, dst = n + 4;
int require = 0;
for(int i = 0; i <= n; ++i) {
if(diff[i] >= 0) {
graph.addedge(src, i + 1, diff[i], 0);
} else {
graph.addedge(i + 1, dst, -diff[i], 0);
require += -diff[i];
}
}
for(auto [u, v] : rngs) {
graph.addedge(v + 1, u, inf, 1);
}
auto [cost, flow] = graph.solve();
if(flow < require) {
cout << "-1\n";
} else {
cout << cost << '\n';
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
// cin >> t;
while(t--) {
solve();
}
return 0;
}
upd:本场比赛在CF讨论区被踩满了,哈哈

浙公网安备 33010602011771号