C. Alternated
最终形式只有 ABAB...AB
或 BABA...BA
将原串中的每个字符 A
按顺序移动到上述形式的相应位置上即可
代码实现
#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; string s;
cin >> n >> s;
ll ans = 1e18;
rep(ri, 2) {
ll now = 0;
int ai = 0;
rep(i, n*2) {
if (s[i] == 'A') {
now += abs(i - (ai*2+ri));
ai++;
}
}
ans = min(ans, now);
}
cout << ans << '\n';
return 0;
}
D. RLE Moving
本题要解决的是,在给定的移动序列中,计算两个人同时处于同一位置的次数。核心思想是跟踪两人之间的相对位置,而不是分别跟踪他们各自的绝对位置。
-
相对位置: 两人位于同一单元格当且仅当他们之间的相对位置是 \((0,0)\)。因此,一开始就计算出高桥相对于青木的初始位置 \((R_t - R_a, C_t - C_a)\)。
-
分段处理: 由于移动序列 \(S\) 和 \(T\) 都是由重复的字符块组成的,他们的移动模式是分段恒定的。采用双指针(或合并)方法来遍历这些移动。将整个
N
次移动分解成一个个小段,每段内高桥和青木的移动方向都保持不变。每段的持续时间 \(w\) 取决于他们当前移动块中剩余的步数。 -
相对速度: 在每一段 \(w\) 次移动中,高桥和青木各自以恒定速度 \(dt\) 和 \(da\) 移动。他们之间的相对速度是 \(d = dt - da\)。在此期间,相对位置从 \(t\) 变为 \(t + d \cdot w\)。
-
寻找重合点: 问题的关键是在每段移动中找出相对位置是否会变为 \((0, 0)\)。相对位置在 \(x\) 步移动后变为 \(t + d \cdot x\)。我们需解方程 \(t + d \cdot x = (0, 0)\),并确保解 \(x\) 满足 \(0 < x \leqslant w\)。这等价于一个线性方程组:
- \(t.r + d.r \cdot x = 0\)
- \(t.c + d.c \cdot x = 0\)
-
求解线性方程:
calc
函数用于解形如 \(ax + b = 0\) 的方程。- 如果 \(a = 0\):仅当 \(b = 0\) 时有解。此时任何 \(x\) 都成立(用
INF
表示)。否则无解(用-1
表示)。 - 如果 `\(a \neq 0\):解为 \(x = -\frac{b}{a}\),但要求是整数解。
- 如果 \(a = 0\):仅当 \(b = 0\) 时有解。此时任何 \(x\) 都成立(用
-
统计次数: 根据
calc
函数对 \(r\) 和 \(c\) 坐标的解进行判断:- 当 \(r\) 和 \(c\) 都有解时:
- 如果
rx == INF
且cx == INF
:这意味着相对位置在该段内始终为 \((0,0)\)。两人在这 \(w\) 步中一直重合,所以ans += w
。 - 如果一个解是
INF
,另一个是有限值(如 \(rx\) 是有限的):这意味着 \(r\) 坐标只在 \(x=rx\) 时重合。如果 \(0 < rx \leqslant w\),那么在此期间有一次重合,所以ans += 1
。 - 如果两个解都是有限值:两人重合仅当
rx == cx
。若此条件成立且 \(0 < rx \leqslant w\),则有一次重合,ans += 1
。
- 如果
- 当 \(r\) 和 \(c\) 都有解时:
-
更新状态: 处理完一段 \(w\) 步后,相对位置 \(t\) 更新为 \(t + d \cdot w\)。然后,移动两个指针 \(\mathrm{ti}\) 和 \(\mathrm{ai}\) 到下一个移动块,重复上述过程,直到遍历完所有移动。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
struct P {
ll r, c;
P operator*(ll a) const { return P(r*a, c*a); }
P operator+(const P& a) const { return P(r+a.r, c+a.c); }
P operator-(const P& a) const { return P(r-a.r, c-a.c); }
};
const ll INF = 1e18;
// ax+b = 0
ll calc(ll a, ll b) {
if (a == 0) return b == 0 ? INF : -1;
if (a < 0) a = -a, b = -b;
if (-b%a) return -1;
return -b/a;
}
int main() {
P t, a;
cin >> t.r >> t.c >> a.r >> a.c;
t = t-a;
ll n; int m, l;
cin >> n >> m >> l;
vector<pair<char, ll>> pt, pa;
rep(_, 2) {
ll now = 0;
rep(i, m) {
char s; ll a;
cin >> s >> a;
now += a;
pt.emplace_back(s, now);
}
swap(pt, pa);
swap(m, l);
}
map<char, P> mp;
mp['U'] = P(-1, 0);
mp['D'] = P(1, 0);
mp['L'] = P(0, -1);
mp['R'] = P(0, 1);
ll ans = 0, now = 0;
int ti = 0, ai = 0;
while (ti < m and ai < l) {
ll nxt = min(pt[ti].second, pa[ai].second);
ll w = nxt-now;
P dt = mp[pt[ti].first];
P da = mp[pa[ai].first];
P d = dt-da;
{
ll rx = calc(d.r, t.r);
ll cx = calc(d.c, t.c);
if (rx != -1 and cx != -1) {
if (rx == INF) swap(rx, cx);
if (rx == INF) {
ans += w;
}
else if (cx == INF) {
if (0 < rx and rx <= w) ans += 1;
}
else {
if (rx == cx and 0 < rx and rx <= w) ans += 1;
}
}
}
t = t + d*w;
if (nxt == pt[ti].second) ti++;
if (nxt == pa[ai].second) ai++;
now = nxt;
}
cout << ans << '\n';
return 0;
}
E. Yacht
记忆化搜索
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
inline void chmax(double& x, double y) { if (x < y) x = y; }
int main() {
int n = 6;
vector<int> a(n);
rep(i, n) cin >> a[i];
vector<map<vector<int>, double>> memo(3);
auto f = [&](auto f, int rem, vector<int> keep) -> double {
if (rem == 0) {
double res = 0;
rep(i, n) {
int cnt = 0;
for (int x : keep) if (x == a[i]) cnt++;
chmax(res, cnt*a[i]);
}
return res;
}
rem--;
if (memo[rem].count(keep)) return memo[rem][keep];
auto g = [&](auto g, int num, vector<int> dice) -> double {
if (num == 0) {
int m = dice.size();
double res = 0;
rep(s, 1<<m) {
vector<int> kp = keep;
rep(i, m) if (s>>i&1) kp.push_back(dice[i]);
chmax(res, f(f, rem, kp));
}
return res;
}
num--;
double res = 0;
dice.push_back(0);
rep(i, n) {
dice.back() = a[i];
res += g(g, num, dice);
}
return res/n;
};
double res = g(g, 5-keep.size(), {});
return memo[rem][keep] = res;
};
double ans = f(f, 3, {});
printf("%.10f\n", ans);
return 0;
}
F. Erase between X and Y
用双向链表来模拟
对于操作 \(2\),同时从 \(x\) 的前驱和后继两个方向来找 \(y\)(因为我们不确定序列中 \(x\) 和 \(y\) 的相对顺序)
代码实现
#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 q;
cin >> q;
vector<int> pre(1, -1), nxt(1, -1);
for (int qi = 1; qi <= q; ++qi) {
int type, x;
cin >> type >> x;
if (type == 1) {
pre.push_back(x); nxt.push_back(nxt[x]);
if (nxt[x] != -1) pre[nxt[x]] = qi;
nxt[x] = qi;
}
else {
int y;
cin >> y;
int l = pre[x], r = nxt[x];
ll ls = 0, rs = 0, ans = 0;
while (l != y and r != y) {
if (l != -1) ls += l, l = pre[l];
if (r != -1) rs += r, r = nxt[r];
}
if (l == y) ans += ls, swap(x, y);
else ans += rs;
nxt[x] = y; pre[y] = x;
cout << ans << '\n';
pre.push_back(-1);
nxt.push_back(-1);
}
}
return 0;
}
G. Increase to make it Increasing
由于多个环节存在供需失衡,需最小化调度成本 \(\to\) 转化为网络流问题
代码实现
分析以下代码的思路:
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> a(n);
rep(i, n) cin >> a[i];
const int INF = 1001001001;
a.push_back(INF);
for (int i = n-1; i >= 0; --i) a[i+1] -= a[i];
int sv = n+1, tv = sv+1, must = 0;
mcf_graph<int, int> g(tv+1);
rep(i, n+1) {
if (a[i] > 0) g.add_edge(sv, i, a[i], 0);
else g.add_edge(i, tv, -a[i], 0), must += -a[i];
}
rep(i, m) {
int l, r;
cin >> l >> r;
g.add_edge(r, l-1, INF, 1);
}
auto [flow, cost] = g.flow(sv, tv);
if (flow != must) cost = -1;
cout << cost << '\n';
return 0;
}