C. Reverse Permutation

\(i\) 的升序依次“添加 \(i\)\(\to\) “执行操作 \(i\)”。用 std::deque 维护当前数组,同时维护是否处于反转状态,并以 \(O(1)\) 时间处理反转。

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

using namespace std;

int main() {
    int n;
    string s;
    cin >> n >> s;
    
    deque<int> a;
    bool rev = false;
    rep(i, n) {
        if (rev) a.push_front(i+1);
        else a.push_back(i+1);
        if (s[i] == 'o') rev = !rev;
    }
    
    if (rev) ranges::reverse(a);
    
    rep(i, n) cout << a[i] << " \n"[i == n-1];
    
    return 0;
}

还有另一种做法:
从后往前决定每个数字放到最终数组的哪一端,利用一个左右指针在数组两端填入位置,维护一个翻转标志 rev 表示当前方向。

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

using namespace std;

int main() {
    int n;
    string s;
    cin >> n >> s;
    
    vector<int> a(n);
    int l = 0, r = n-1; bool rev = false;
    for (int i = n-1; i >= 0; --i) {
        if (s[i] == 'o') rev = !rev;
        if (!rev) a[r--] = i; else a[l++] = i;
    }
    
    rep(i, n) cout << a[i]+1 << " \n"[i == n-1];
    
    return 0;
}

D. X to Y

这题其实就是把 P13016 的父节点简化了

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

using namespace std;
using ll = long long;

void solve() {
    ll x, y, k;
    cin >> x >> y >> k;
    
    int ans = 0;
    while (x != y) {
        if (x > y) x /= k; else y /= k;
        ans++;
    }
    
    cout << ans << '\n';
}

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

E. Digit Circus

数位dp
dp[i][j][k][s] 表示

  • 已经处理了前 \(i\)
  • \(j=0\) 表示当前已构造的前缀与 \(N\) 的前 \(i\) 位完全相同,\(j=1\) 表示已经完全小于 \(N\) 的前缀
  • 当前前缀对应的数模 \(3\) 的余数为 \(k\)
  • 当前前缀对应的数集为 \(s\)

这里时间给的比较宽裕,完全可以用 std::map 来实现

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

using namespace std;
using ll = long long;
using mint = modint998244353;

int main() {
    string N;
    cin >> N;
    
    using T = tuple<int, int, int>;
    map<T, mint> dp;
    dp[T(0, 0, 0)] = 1;
    for (char c : N) {
        c -= '0';
        map<T, mint> old;
        swap(dp, old);
        for (auto [t, x] : old) {
            auto [j, k, s] = t;
            rep(d, 10) {
                int nj = j, nk = (k*10+d)%3, ns = s|1<<d;
                if (nj == 0 and d > c) continue;
                if (d < c) nj = 1;
                if (s == 0 and d == 0) ns = 0;
                dp[T(nj, nk, ns)] += x;
            }
        }
    }
    
    mint ans;
    for (auto [t, x] : dp) {
        auto [j, k, s] = t;
        int cnt = 0;
        if (k == 0) cnt++;
        if (s>>3&1) cnt++;
        if (__builtin_popcount(s) == 3) cnt++;
        if (cnt == 1) ans += x;
    }
    ans -= 1;
    
    cout << ans.val() << '\n';
    
    return 0;
}

F. Sjeltzer?

本题的核心突破口在于饮品 ID 的长度只有 \(6\) 位。这意味着所有可能的 ID 组合总共只有 \(10^6\) 种。

由于每一位数字之间是相互独立的,我们可以把一个 \(6\) 位数的 ID 看作是六维空间中的一个坐标点,每个维度的取值范围都是 \(0\)\(9\)。题目要求的查询,本质上是在这个六维空间中,求一个“超矩形区域” \([x_1, y_1] \times [x_2, y_2] \times \dots \times [x_6, y_6]\) 内所有点权值的总和。

为了快速回答大量询问,预处理高维前缀和,并在查询时利用多维容斥原理进行 \(O(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;
    cin >> n;
    
    int m = 6;
    vector<int> ten(m+1, 1);
    rep(i, m) ten[i+1] = ten[i]*10;
    int M = ten[m];
    
    vector<ll> d(M);
    rep(i, n) {
        int s, v;
        cin >> s >> v;
        d[s] += v;
    }
    
    rep(k, m) {
        rep(i, M) {
            if (i/ten[k]%10 == 9) continue;
            d[i+ten[k]] += d[i];
        }
    }
    
    int q;
    cin >> q;
    rep(qi, q) {
        int x, y;
        cin >> x >> y;
        ll ans = 0;
        rep(s, 1<<m) {
            int r = 0, sign = 1;
            rep(i, m) {
                int c = y/ten[i]%10;
                if (s>>i&1) {
                    sign = -sign;
                    c = min(c, x/ten[i]%10-1);
                }
                if (c < 0) sign = 0;
                else r += c*ten[i];
            }
            if (r < 0) continue;
            ans += d[r]*sign;
        }
        cout << ans << '\n';
    }
    
    return 0;
}