A. Cut

模拟

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

using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    
    rotate(a.begin(), a.begin()+(n-k), a.end());
    
    rep(i, n) cout << a[i] << ' ';
    
    return 0;
}

B. Decrease 2 max elements

模拟

代码实现
#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);
    rep(i, n) cin >> a[i];
    
    int ans = 0;
    while (1) {
        ranges::sort(a, greater<>());
        if (a[1] <= 0) break;
        a[0]--; a[1]--;
        ans++;
    }
    
    cout << ans << '\n';
    
    return 0;
}

C. Triple Attack

周期性

把 {1, 1, 3} 看成是一个组合技,用这 \(3\) 次攻击可以让敌人掉 \(5\) 点血
对每个敌人先尽可能地用组合技,如果还有剩余血量,就暴力模拟即可

代码实现
#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;
    cin >> n;
    
    vector<int> h(n);
    rep(i, n) cin >> h[i];
    
    ll t = 0;
    rep(i, n) {
        int x = h[i]/5;
        t += x*3;
        h[i] -= x*5;
        while (h[i] > 0) {
            t++;
            if (t%3 == 0) h[i] -= 3;
            else h[i]--;
        }
    }
    
    cout << t << '\n';
    
    return 0;
}

D. Minimum Steiner Tree

显然应该从叶子节点开始删,可以考虑以某个关键点跑dfs
一个点被保留当且仅当以它为根的子树中包含关键点

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

using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    
    vector<vector<int>> to(n);
    rep(i, n-1) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        to[a].push_back(b);
        to[b].push_back(a);
    }
    
    vector<int> vs(k);
    rep(i, k) cin >> vs[i], vs[i]--;
    
    vector<bool> selected(n);
    rep(i, k) selected[vs[i]] = true;
    
    vector<int> num(n);
    auto dfs = [&](auto& f, int v, int p=-1) -> void {
        if (selected[v]) num[v]++;
        for (int u : to[v]) {
            if (u == p) continue;
            f(f, u, v);
            num[v] += num[u];
        }
    };
    dfs(dfs, vs[0]);
    
    int ans = 0;
    rep(i, n) if (num[i] > 0) ans++;
    
    cout << ans << '\n';
    
    return 0;
}

E. Train Delay

注意到,\( T_i + X_i \leqslant S_j + X_j \Leftrightarrow T_i + X_i - S_j \leqslant X_j \)
于是,可以得到 \(X_j = \max(T_i + X_i) - S_j\)
可以按 \(S_j\) 递增的顺序来求出所有的 \(X_i\),暴力模拟的时间复杂度为 \(\mathcal{O}(M^2)\)

下面考虑优化:
对每个站点维护一个二元组序列 \(\{(T_i, T_i+X_i)\}\) 的信息
对于 \(\max(T_i+X_i)\),可以开一个数组 maxT 来维护,\(maxT[a]\) 就表示到站时间在当前火车的发车时间之前且目的地在站点 \(a\) 处的 \(\max(T_i + X_i)\)
可以考虑用小根堆来维护每个站点的二元组序列,这样就能保证 \(T_i\) 单调递增了

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

using namespace std;
using P = pair<int, int>;
using PQ = priority_queue<P, vector<P>, greater<P>>;

struct Train {
    int a, b, s, t, i;
    Train(int a, int b, int s, int t, int i): a(a), b(b), s(s), t(t), i(i) {}
    bool operator<(const Train& o) const {
        return s < o.s;
    }
};

int main() {
    int n, m, x1;
    cin >> n >> m >> x1;
    
    vector<Train> trains;
    rep(i, m) {
        int a, b, s, t;
        cin >> a >> b >> s >> t;
        --a; --b;
        trains.emplace_back(a, b, s, t, i);
    }
    
    sort(trains.begin(), trains.end());
    
    vector<int> x(m);
    vector<int> maxT(n);
    vector<PQ> q(n);
    for (auto [a, b, s, t, i] : trains) {
        if (i == 0) x[i] = x1;
        else {
            while (q[a].size()) {
                auto [nt, val] = q[a].top(); 
                if (nt > s) break;
                q[a].pop();
                maxT[a] = max(maxT[a], val);
            }
            x[i] = max(0, maxT[a]-s);
        }
        q[b].emplace(t, t+x[i]);
    }
    
    rep(i, m) if (i) cout << x[i] << ' ';
    
    return 0;
}

F. Dividing Game

对每个数求一遍 \(sg\) 函数,打表可以发现 \(sg(x)\) 就是 \(x\) 的可重复质因子集合的大小,然后直接套 \(sg\) 定理即可

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

using namespace std;

int main() {
    const int M = 100005;
    vector<int> g(M);
    for (int i = 1; i < M; ++i) {
        int now = 0;
        int x = i;
        for (int d = 2; d*d <= x; ++d) {
            if (x%d != 0) continue;
            while (x%d == 0) now++, x /= d;
        }
        if (x != 1) now++;
        g[i] = now;
    }
    
    int n;
    cin >> n;
    
    int x = 0;
    rep(i, n) {
        int a; cin >> a;
        x ^= g[a];
    }
    
    if (x == 0) puts("Bruno");
    else puts("Anna");
    
    return 0;
}

G. Add and Multiply Queries

黑体字是突破口,注意到询问3的答案不超过 \(1e18\) 就很简单了
如果区间 \([l, r]\) 中的 \(B_i \neq 0\) 的个数超过 \(60\) 个,那么执行乘法运算的话显然会超过 \(1e18\),所以我们可以大胆断定区间中 \(B_i \neq 0\) 的位置不超过 \(60\)
对于 \(B_i = 1\) 的连续区间只需执行加法即可

考虑维护满足以下需求的数据结构:

  • 求出下一个满足 \(B_i \geqslant 2\) 的位置 \(i\) \(\to\) std::set
  • 区间中 \(A\) 的累加和 \(\to\) 树状数组
代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    cin.tie(nullptr) -> sync_with_stdio(false);
    
    int n;
    cin >> n;
    
    vector<int> a(n), b(n);
    rep(i, n) cin >> a[i];
    rep(i, n) cin >> b[i];
    
    fenwick_tree<ll> d(n);
    rep(i, n) d.add(i, a[i]);
    set<int> st;
    rep(i, n) if (b[i] > 1) st.insert(i);
    st.insert(n);
    
    int q;
    cin >> q;
    rep(qi, q) {
        int type;
        cin >> type;
        if (type == 3) {
            int l, r;
            cin >> l >> r;
            --l;
            ll v = 0;
            while (l < r) {
                int i = *st.lower_bound(l);
                i = min(i, r);
                v += d.sum(l, i);
                if (i == r) break;
                v = max(v+a[i], v*b[i]);
                l = i+1;
            }
            cout << v << '\n';
        }
        else {
            int i, x;
            cin >> i >> x;
            --i;
            if (type == 1) {
                d.add(i, x-a[i]);
                a[i] = x;
            }
            else {
                b[i] = x;
                if (b[i] > 1) st.insert(i); else st.erase(i);
            }
        }
    }
    
    return 0;
}