C. Alternated

最终形式只有 ABAB...ABBABA...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

本题要解决的是,在给定的移动序列中,计算两个人同时处于同一位置的次数。核心思想是跟踪两人之间的相对位置,而不是分别跟踪他们各自的绝对位置。

  1. 相对位置: 两人位于同一单元格当且仅当他们之间的相对位置是 \((0,0)\)。因此,一开始就计算出高桥相对于青木的初始位置 \((R_t - R_a, C_t - C_a)\)

  2. 分段处理: 由于移动序列 \(S\)\(T\) 都是由重复的字符块组成的,他们的移动模式是分段恒定的。采用双指针(或合并)方法来遍历这些移动。将整个 N 次移动分解成一个个小段,每段内高桥和青木的移动方向都保持不变。每段的持续时间 \(w\) 取决于他们当前移动块中剩余的步数。

  3. 相对速度: 在每一段 \(w\) 次移动中,高桥和青木各自以恒定速度 \(dt\)\(da\) 移动。他们之间的相对速度是 \(d = dt - da\)。在此期间,相对位置从 \(t\) 变为 \(t + d \cdot w\)

  4. 寻找重合点: 问题的关键是在每段移动中找出相对位置是否会变为 \((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\)
  5. 求解线性方程: calc 函数用于解形如 \(ax + b = 0\) 的方程。

    • 如果 \(a = 0\):仅当 \(b = 0\) 时有解。此时任何 \(x\) 都成立(用 INF 表示)。否则无解(用 -1 表示)。
    • 如果 `\(a \neq 0\):解为 \(x = -\frac{b}{a}\),但要求是整数解。
  6. 统计次数: 根据 calc 函数对 \(r\)\(c\) 坐标的解进行判断:

    • \(r\)\(c\) 都有解时
      • 如果 rx == INFcx == 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
  7. 更新状态: 处理完一段 \(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;
}