A. Candy Button

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n, c;
    cin >> n >> c;
    
    vector<int> t(n);
    rep(i, n) cin >> t[i];
    
    int ans = 0, pre = -c;
    rep(i, n) {
        if (t[i]-pre < c) continue;
        pre = t[i];
        ans++;
    }
    
    cout << ans << '\n';
    
    return 0;
}

B. Hands on Ring (Easy)

对于左手而言,如果它和目标位置之间有右手存在的话,那只能走另一个方向,否则左手走到目标位置的距离就是二者的差值
右手也是类似

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int f(int n, int s, int t, int x) {
    if (s > t) swap(s, t);
    if (s < x and x < t) return n-(t-s);
    return t-s;
}

int main() {
    int n, q;
    cin >> n >> q;
    
    int l = 0, r = 1;
    int ans = 0;
    rep(qi, q) {
        char h; int t;
        cin >> h >> t;
        --t;
        
        if (h == 'L') {
            ans += f(n, l, t, r);
            l = t;
        }
        else {
            ans += f(n, r, t, l);
            r = t;
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

C. Prepare Another Box

二分答案

关于判定,先对 \(a\) 做升序排序,然后对将 \(x\) 加入数组 \(b\) 后对 \(b\) 进行排序,\(a_i \to b_i\) 一一对应即可

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n), b(n-1);
    rep(i, n) cin >> a[i];
    rep(i, n-1) cin >> b[i];
    
    ranges::sort(a);
    
    const int INF = 1001001001;
    int ac = INF, wa = 0;
    while (ac-wa > 1) {
        int wj = (ac+wa)/2;
        
        auto ok = [&]{
            auto nb = b;
            nb.push_back(wj);
            ranges::sort(nb);
            rep(i, n) if (a[i] > nb[i]) return false;
            return true;
        }();
        
        (ok ? ac : wa) = wj;
    }
    
    if (ac == INF) ac = -1;
    cout << ac << '\n';
    
    return 0;
}

D. Cycle

先跑一遍BFS求出从点 \(1\) 开始出发到每个点的最短路
对于包含点 \(1\) 的环,一定存在某条有向边指向点 \(1\),假设由点 \(v\) 指向点 \(1\),那么答案就是 \(\min\{\operatorname{dist}[v]+1\}\)

代码实现
#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<vector<int>> to(n);
    rep(i, m) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        to[a].push_back(b);
    }
    
    const int INF = 1001001001;
    vector<int> dist(n, INF);
    queue<int> q;
    dist[0] = 0; q.push(0);
    while (q.size()) {
        int v = q.front(); q.pop();
        for (int u : to[v]) {
            if (dist[u] != INF) continue;
            dist[u] = dist[v]+1;
            q.push(u);
        }
    }
    
    int ans = INF;
    rep(v, n) {
        for (int u : to[v]) {
            if (u == 0) {
                ans = min(ans, dist[v]+1);
            }
        }
    }
    
    if (ans == INF) ans = -1;
    cout << ans << '\n';
    
    return 0;
}

E. Max × Sum

可以先对 \((A_i, B_i)\)\(A_i\) 的大小做升序排序
然后遍历 \(i = k, k+1, \cdots, n\)\(A_i\) 就是当前的最大值,然后从 \(B\) 的前面 \(i-1\) 个数中选出前 \(k-1\) 小的 \(B_j\)
那么现在问题变成了如果找集合里的前 \(k-1\) 小的数的和
考虑实现以下功能的数据结构:

  • 添加值
  • 删除最大值

可以通过维护大小为 \(k-1\) 的大根堆来实现这两个功能

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;
using P = pair<int, int>;

void solve() {
    int n, k;
    cin >> n >> k;
    
    vector<P> ab(n);
    rep(i, n) cin >> ab[i].first;
    rep(i, n) cin >> ab[i].second;
    
    ranges::sort(ab);
    
    ll ans = 1e18;
    ll sum = 0;
    priority_queue<int> q;
    for (auto [a, b] : ab) {
        sum += b;
        if (q.size() >= k-1) {
            ans = min(ans, sum*a);
        }
        q.push(b);
        if (q.size() == k) sum -= q.top(), q.pop();
    }
    
    cout << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

F. Hands on Ring (Hard)

第一想法是考虑如下dp
dp[i][L][R] 表示到第 \(i\) 个操作为止,左手在位置 \(L\) 且右手在位置 \(R\) 处的最小移动代价
可以发现光是状态数就已经爆掉了

注意到,\(H_i\) 对应的手的目标位置已经确定了
dp[i][j] 表示到第 \(i\) 个操作为止不是 \(H_i\) 的手的位置为 \(j\) 时的最小移动代价
转移只需考虑“另一只手不动避开”以及“推着另一只手移动”这两种转移方式

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;
using P = pair<int, int>;

inline void chmin(int& x, int y) { if (x > y) x = y; }

int main() {
    int n, q;
    cin >> n >> q;
    
    const int INF = 1001001001;
    vector<int> dp(n, INF);
    char lh = 'L'; int lt = 0;
    dp[1] = 0;
    
    rep(qi, q) {
        char h; int t;
        cin >> h >> t;
        --t;
        
        rotate(dp.begin(), dp.begin()+lt, dp.end());
        t = (t-lt+n)%n;
        vector<int> old(n, INF);
        swap(dp, old);
        
        rep(side, 2) {
            rep(i, n) {
                if (h == lh) {
                    if (i <= t) {
                        chmin(dp[(t+1)%n], old[i] + (t + (t+1)-i));
                    }
                    else {
                        chmin(dp[i], old[i] + t);
                    }
                }
                else {
                    if (i <= t) {
                        chmin(dp[0], old[i] + (t-i));
                    }
                    else {
                        chmin(dp[(t+1)%n], old[i] + (n-i+t) + (t+1));
                    }
                }
            }
            
            reverse(dp.begin()+1, dp.end());
            reverse(old.begin()+1, old.end());
            t = (n-t)%n;
        }
        
        rotate(dp.begin(), dp.begin()+(n-lt), dp.end());
        lh = h; lt = (lt+t)%n;
    }
    
    int ans = INF;
    rep(i, n) chmin(ans, dp[i]);
    cout << ans << '\n';
    
    return 0;
}