CF2004G 題解

CTHOOH發表於2024-09-19
題意

定義關於數字串 \(s\) 的函式 \(f(s)\) 表示將 \(t\) 切分為 \(m\) 段,要求 \(m\) 是偶數,假設這些字串分別為 \(t_1, t_2, \ldots, t_m\),有 \(s = t_1 + t_2 + \ldots + t_m\)。定義 \(A^x\) 表示 \(\underbrace{AA\ldots AA}_{x~\text{times}}\),求一種劃分方式,使得 \(t_2^{t_1} + t_4^{t_3} + \ldots + t_m^{t_{m - 1}}\) 最短。

現在給定長度為 \(n\) 的字串 \(S\) 和一個正整數 \(k\),對每個 \(i = 1, 2, \ldots, n - k + 1\),求出 \(f(s[i, i + 1, \ldots, i + k - 1])\)

首先考慮 \(k = n\) 的情況,容易觀察到對於 \(i\) 為奇數,\(t_i\) 一定小於 \(10\),否則可以將後一位分給 \(t_{i + 1}\),一定更優。根據該性質,可以考慮 dp 並設計出 dp 方程,設 \(f_{i, j}\) 表示考慮前 \(i\) 位,現在的 \(t_{k}\) 等於 \(j\) 的答案,有轉移:

  • 剛剛結束一段 \(t_k\),則 \(j = 0\),有 \(f_{i, j} = \min\{f_{i - 1, k} + k\}\),其中 \(k \in [1, 9]\)
  • 延續關於 \(t_k\) 的選擇,對於 \(j \in [1, 9]\),有 \(f_{i, j} = \min\{f_{i - 1, j} + j\}\)
  • 開始一段新的 \(t_k\),有 \(f_{i, t_j} = \min\{f_{i - 1, 0}\}\)

答案是 \(f_{n, 0}\)

注意到這個轉移是可以矩乘輔助轉移的,可以設計出矩陣:

\[\begin{bmatrix} f_{i, 0} & f_{i, 1} & f_{i, 2} & \ldots & f_{i, 9} \end{bmatrix} \times \begin{bmatrix} +\infty & +\infty & +\infty & \ldots & +\infty \\ 1 & 1 & +\infty & \ldots & +\infty\\ 2 & +\infty & 2 & \ldots & +\infty\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 9 & +\infty & +\infty & \ldots & 9 \end{bmatrix} = \begin{bmatrix} f_{i + 1, 0} & f_{i + 1, 1} & f_{i + 1, 2} & \ldots & f_{i + 1, 9} \end{bmatrix} \]

要把轉移矩陣的第一行第 \(t_i\) 列設成 \(0\),注意這裡的矩陣乘法為 \((\min, +)\) 運算,即 \(A \times B = C\) 實際上是:

\[C_{i, j} = \min_{k = 0}^9 A_{i, k} + B_{k, j} \]

\(w = 10\),可以在 \(O(nw^3)\) 的複雜度內計算出 \(k = n\) 時的答案,考慮擴充套件到 \(k < n\) 的情況。

有一個經典 trick(這個 trick 在 P6717P1117 中都有運用),就是將序列按照塊長為 \(k - 1\) 分為若干塊,這樣每一個長度為 \(k\) 的子區間一定都是一塊的字首和一塊的字尾拼起來的。

所以可以預處理每個塊的字首積和字尾積,每次查詢時乘起來即可。時間複雜度仍為 \(O(nw^3)\)

\(O(nw^3)\) 常數過大,寫出來會被卡常,考慮最佳化。由於矩陣中只有 \(O(w)\) 個位置不是 \(+\infty\),所以對這 \(O(w)\) 個位置分別轉移即可,時間複雜度最佳化到了 \(O(nw^2)\),可以透過。

程式碼
#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;

constexpr int N = 10;

constexpr int inf = 1E9;

struct Matrix {
    std::vector<std::vector<int>> a;

    Matrix() {
        Init(0);
    }
    Matrix(int k) {
        Init(k);
    }

    void Init(int k) {
        a.assign(N, std::vector<int>(N, inf));
        if (k == 1) {
            for (int i = 0; i < N; ++i) {
                a[i][i] = 0;
            }
        }
    }

    auto &operator[](int x) {
        return a[x];
    }
} ;

Matrix appendr(Matrix pre, int x) {
    Matrix res;
    
    for (int i = 0; i < N; ++i) {
        for (int j = 1; j < N; ++j) {
            res[i][0] = std::min(res[i][0], pre[i][j] + j);
            res[i][j] = std::min(res[i][j], pre[i][j] + j);
        }
    }
    for (int i = 0; i < N; ++i) {
        res[i][x] = std::min(res[i][x], pre[i][0]);
    }

    return res;
}

Matrix appendl(Matrix pre, int x) {
    Matrix res;

    for (int i = 1; i < N; ++i) {
        for (int j = 0; j < N; ++j) {
            res[i][j] = std::min(res[i][j], i + std::min(pre[0][j], pre[i][j]));
        }
    }

    for (int i = 0; i < N; ++i) {
        res[0][i] = std::min(res[0][i], pre[x][i]);
    }

    return res;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, k;
    std::cin >> n >> k;
    
    std::string s;
    std::cin >> s;

    std::vector<Matrix> suf(n);

    for (int l = 0, r = 0; l < n; l = r + 1) {
        r = std::min(n - 1, l + k - 2);

        Matrix res = 1;
        for (int i = l; i <= r; ++i) {
            res = appendr(res, s[i] - '0');
            if (i + 1 >= k) {                  
                int ans = inf;
                for (int x = 0; x < N; ++x) {
                    ans = std::min(ans, suf[i - k + 1][0][x] + res[x][0]);
                }
                std::cout << ans << " ";
            }
        }

        suf[r] = 1;
        for (int i = r; i >= l; --i) {
            suf[i] = appendl(suf[i + (i < r)], s[i] - '0');
        }
    }

    return 0;
}