A. 369

\(A\)\(B\) 之间可以插一个数构成等差数列当且仅当 \(A+B\) 是偶数

特例:\(A=B\),此时答案是 \(1\)

代码实现
a, b = map(int, input().split())
if a == b: print(1)
elif (a+b)%2 == 0: print(3)
else: print(2)

B. Piano 3

把左手和右手分别放在对应的最左侧然后按顺序模拟即可

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

using namespace std;

int main() {
    int n;
    cin >> n;
    
    int ans = 0;
    int l = -1, r = -1;
    rep(i, n) {
        int a; char s;
        cin >> a >> s;
        if (s == 'L') {
            if (l != -1) ans += abs(l-a);
            l = a;
        }
        else {
            if (r != -1) ans += abs(r-a);
            r = a;
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

C. Count Arithmetic Subarrays

双指针

代码实现
#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> a(n);
    rep(i, n) cin >> a[i];
    
    ll ans = 0;
    int r = 0;
    rep(l, n) {
        while (r < n) {
            if (r > l+1 and a[r]-a[r-1] != a[r-1]-a[r-2]) break;
            ++r;
        }
        ans += r-l;
    }
    
    cout << ans << '\n';
    
    return 0;
}

D. Bonus EXP

简单dp
dp[i][j] 表示到第 \(i\) 只怪兽为止已经打的怪兽的个数 \(\% ~2 = j\) 时能获得的最大经验值

时间复杂度为 \(\mathcal{O}(N)\)

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

using namespace std;
using ll = long long;

inline void chmax(ll& x, ll y) { if (x < y) x = y; }

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    
    const ll INF = 1e18;
    vector dp(n+1, vector<ll>(2, -INF));
    dp[0][0] = 0;
    rep(i, n) {
        int ni = i+1;
        rep(j, 2) {
            { // o
                int nj = j^1;
                int X = j%2 ? a[i]*2 : a[i];
                chmax(dp[ni][nj], dp[i][j]+X);
            }
            { // x
                int nj = j;
                chmax(dp[ni][nj], dp[i][j]);
            }
        }
    }
    
    ll ans = max(dp[n][0], dp[n][1]);
    cout << ans << '\n';
    
    return 0;
}

E. Sightseeing Tour

注意到 \(k \leqslant 5\) 这个条件,这道题就做完了
只需枚举这 \(k\) 条边不同的顺序和每条边的方向
\(n \leqslant 400\) 暗示我们可以跑 \(\operatorname{floyd}\)
那么复杂度就是 \(\mathcal{O}(n^3 + qk!2^k)\)

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

using namespace std;
using ll = long long;
using Edge = tuple<int, int, int>;

inline void chmin(ll& a, ll b) { if (a > b) a = b; }

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<Edge> es;
    const ll INF = 1e18;
    vector dist(n, vector<ll>(n, INF));
    rep(i, n) dist[i][i] = 0;
    rep(i, m) {
        int a, b, c;
        cin >> a >> b >> c;
        --a; --b;
        es.emplace_back(a, b, c);
        chmin(dist[a][b], c);
        chmin(dist[b][a], c);
    }
    
    rep(k, n)rep(i, n)rep(j, n) chmin(dist[i][j], dist[i][k]+dist[k][j]);
    
    int q;
    cin >> q;
    rep(qi, q) {
        int k;
        cin >> k;
        vector<int> ei(k);
        rep(i, k) cin >> ei[i], ei[i]--;
        ll ans = INF;
        do {
            rep(s, 1<<k) {
                ll now = 0;
                int v = 0;
                rep(i, k) {
                    auto [a, b, c] = es[ei[i]];
                    if (s>>i&1) swap(a, b);
                    now += dist[v][a];
                    now += c;
                    v = b;
                }
                now += dist[v][n-1];
                chmin(ans, now);
            }
        } while (next_permutation(ei.begin(), ei.end()));
        cout << ans << '\n';
    }
    
    return 0;
}

F. Gather Coins

这题本质上就是求二维上升子序列
可以用扫描线和线段树加速

代码实现
#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 P = pair<int, int>;

P op(P a, P b) { return max(a, b); }
P e() { return P(0, -1); }

int main() {
    int h, w, n;
    cin >> h >> w >> n;
    
    vector<P> coins;
    rep(i, n) {
        int r, c;
        cin >> r >> c;
        coins.emplace_back(r, c);
    }
    ranges::sort(coins);
    
    const int MX = 200005;
    segtree<P, op, e> t(MX);
    vector<int> pre(n);
    rep(i, n) {
        int y = coins[i].second;
        auto [lis, idx] = t.prod(0, y+1);
        pre[i] = idx;
        t.set(y, P(lis+1, i));
    }
    
    string ans;
    int r = h, c = w;
    auto mv = [&](P p) {
        auto [nr, nc] = p;
        while (r > nr) r--, ans += 'D';
        while (c > nc) c--, ans += 'R';
    };
    auto [lis, i] = t.all_prod();
    while (i != -1) {
        mv(coins[i]);
        i = pre[i];
    }
    mv(P(1, 1));
    ranges::reverse(ans);
    
    cout << lis << '\n';
    cout << ans << '\n';
    
    return 0;
}

G. As far as possible

可以发现答案就是由点 \(1\) 和指定的 \(k\) 个点构成的树形图上每条边的边权 \(\times 2\) 的总和
青木为了让答案最大,应该尽可能地选距离点 \(1\) 更远的叶子节点,等选完叶子节点以后,剩下的答案保持不变
那么怎么来求答案呢?

  • 树形dp+multiset做dsu on tree

我们可以对树上每个点维护一个可重集,对于 \(v \to u\) 这条边,边权为 \(w\),取出点 \(u\) 的可重集中的最大值 \(x\),同时将这个最大值在点 \(u\) 的可重集中删去,然后将 \(x+w\) 加入点 \(v\) 的可重集中

具体分析可以参考 \(\operatorname{t\color{\red}{iger2005}}\)题解

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

using namespace std;
using ll = long long;

struct Edge {
    int to, cost;
    Edge() {}
    Edge(int to, int cost): to(to), cost(cost) {}
};

int main() {
    int n;
    cin >> n;
    
    vector<vector<Edge>> g(n);
    rep(i, n-1) {
        int a, b, c;
        cin >> a >> b >> c;
        --a; --b;
        g[a].emplace_back(b, c);
        g[b].emplace_back(a, c);
    }
    
    auto dfs = [&](auto& dfs, int v, int p=-1) -> multiset<ll> {
        multiset<ll> f;
        for (auto [u, w] : g[v]) {
            if (u == p) continue;
            auto nf = dfs(dfs, u, v);
            
            ll x = *nf.rbegin(); nf.erase(prev(nf.end()));
            nf.insert(x+w);
            
            if (f.size() < nf.size()) swap(f, nf);
            f.merge(nf);
        }
        f.insert(0);
        return f;
    };
    
    auto f = dfs(dfs, 0);
    vector<ll> as;
    for (ll x : f) as.push_back(x);
    ranges::reverse(as);
    
    ll ans = 0;
    for (ll a : as) {
        cerr << a << '\n';
        ans += a*2;
        cout << ans << '\n';
    }
    
    return 0;
}