C. Third Largest Number

维护前 \(3\) 大的数,比较好的实现是开一个大小为 \(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;
    
    vector<int> d(3);
    rep(i, n) {
        int a;
        cin >> a;
        d.push_back(a);
        ranges::sort(d, greater<>());
        d.pop_back();
        if (i >= 2) cout << d[2] << '\n';
    }
    
    return 0;
}

D. Automat

先尽可能多地(按价格从小到大)把能“仅用 \(K\) 钞”买下的饮料取出(这些饮料不会消耗 \(1\) 元票据),把它们当成普通商品和甜点合并在一起;
然后把所有剩下的商品(甜点 + 已取出的饮料)按价格从小到大买,使用总体的“货币总量”上限 \(x + y*K\)(把所有钱都看成可自由使用的等价货币)来判断能买多少件。

代码实现
#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, m, k;
    ll x, y;
    cin >> n >> m >> k >> x >> y;
    
    vector<int> a(n), b(m);
    rep(i, n) cin >> a[i];
    rep(i, m) cin >> b[i];
    
    ranges::sort(b);
    
    {
        int r = 0;
        ll used = 0;
        while (r < m) {
            used += (b[r]+k-1)/k;
            if (used > y) break;
            ++r;
        }
        rep(i, r) a.push_back(b[i]);
    }
    
    ranges::sort(a);
    
    int ans = 0;
    ll tot = 0;
    while (ans < a.size()) {
        tot += a[ans];
        if (tot > x+y*k) break;
        ans++;
    }
    
    cout << ans << '\n';
    
    return 0;
}

E. Min-Max Swap

开两颗线段树用来维护区间最小值和区间最大值,至于它们对应的位置可以用线段树二分

但考虑到 \(P\) 是一个排列,于是我们只需维护一个位置数组即可

代码实现
#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 min_(int a, int b) { return min(a, b); }
int max_(int a, int b) { return max(a, b); }
int e_min() { return 1e9; }
int e_max() { return 0; }

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> p(n);
    rep(i, n) cin >> p[i];
    
    segtree<int, min_, e_min> t_min(p);
    segtree<int, max_, e_max> t_max(p);
    
    vector<int> q(n+1);
    rep(i, n) q[p[i]] = i;
    
    rep(i, m) {
        int l, r;
        cin >> l >> r;
        --l;
        
        int a = t_min.prod(l, r);
        int b = t_max.prod(l, r);
        int ai = q[a], bi = q[b];
        swap(p[ai], p[bi]);
        swap(q[a], q[b]);
        t_min.set(ai, p[ai]); t_min.set(bi, p[bi]);
        t_max.set(ai, p[ai]); t_max.set(bi, p[bi]);
    }
    
    rep(i, n) cout << p[i] << " \n"[i == n-1];
    
    return 0;
}

F. Chebyshev Cafe

原坐标系下的切比雪夫距离,等于坐标映射为 \((x+y, x-y)\) 后的曼哈顿距离的一半。

由于曼哈顿距离的 \(u\) 轴与 \(v\) 轴完全独立,问题降维为两个一维问题:

  • 计算网格每点人数 \(C(i, j) = (A_i B_j) \bmod M\),并分别按 \(u = i+j\) 和 \(v = i-j\) 统计各轴上的总人数 \(S_u[u]\) 与 \(S_v[v]\)。
  • 计算各轴上的一维总距离:
    • \(T_u[u] = \sum S_u[u'] \cdot \vert{}u - u'\vert{}\)
    • \(T_v[v] = \sum S_v[v'] \cdot \vert{}v - v'\vert{}\)
  • 点 \((i, j)\) 的总花费即为 \(f(i, j) = \frac{T_u[i+j] + T_v[i-j]}{2}\),遍历累加 \(f(i,j) + (i-1)N + (j-1)\) 的异或和即可。
代码实现
#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, m;
    cin >> n >> m;
    
    vector<ll> a(n), b(n);
    rep(i, n) cin >> a[i];
    rep(i, n) cin >> b[i];
    
    vector c(n, vector<int>(n));
    rep(i, n)rep(j, n) {
        c[i][j] = a[i]*b[j]%m;
    }
    
    vector d(n, vector<ll>(n));
    rep(ri, 2) {
        vector<ll> s(n*2);
        rep(i, n)rep(j, n) s[i+j] += c[i][j];
        vector<ll> t(n*2);
        rep(i, n*2)rep(j, n*2) t[i] += s[j]*abs(i-j);
        rep(i, n)rep(j, n) d[i][j] += t[i+j];
        
        ranges::reverse(c);
        ranges::reverse(d);
    }
    rep(i, n)rep(j, n) d[i][j] /= 2;
    
    ll ans = 0;
    rep(i, n)rep(j, n) {
        ans ^= d[i][j] + n*i + j;
    }
    
    cout << ans << '\n';
    
    return 0;
}

G. Increasing Popcount

先把原题变成 \(\text{popcount}\) 序列的 \(\text{LNIS}\),再把区间拆成二进制对齐块;每个块内部用 \(\text{Sperner}\) 定理说明最优方案只需选一个 \(\text{popcount}\) 层,而层大小就是 \(\binom{k}{r}\),最后对“上一层的 \(\text{popcount} \geqslant\) 当前层”做 \(61\) 个状态的 \(\text{dp}\) 。

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

using namespace std;
using ll = long long;

const int B = 60;

ll comb[B+1][B+1];

void init() {
    comb[0][0] = 1;
    rep(i, B)rep(j, i+1) {
        comb[i+1][j] += comb[i][j];
        comb[i+1][j+1] += comb[i][j];
    }
}

void solve() {
    ll l, r;
    cin >> l >> r;
    
    vector<ll> dp(B+1);
    ll x = l;
    while (x <= r) {
        int k = __builtin_ctzll(x);
        ll len = 1ll<<k;
        while (x+len-1 > r) {
            len >>= 1;
            --k;
        }
        int d = __builtin_popcountll(x>>k);
        vector<ll> suf(B+2);
        for (int i = B; i >= 0; --i) {
            suf[i] = max(suf[i+1], dp[i]);
        }
        
        auto old = dp;
        rep(j, k+1) {
            int i = d+j;
            dp[i] = max(dp[i], suf[i]+comb[k][j]);
        }
        x += len;
    }
    
    ll ans = ranges::max(dp);
    cout << ans << '\n';
}

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