← 返回题目页

T0888 着色

标程

#include <bits/stdc++.h>
using namespace std;

const long long NEG = -(1LL << 60);

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

    int n, m, k, id;
    cin >> n >> m >> k >> id;
    string s;
    cin >> s;
    vector<long long> p(n + 1);
    for (int i = 1; i <= n; i++) cin >> p[i];
    vector<string> pat(m);
    vector<long long> w(m);
    vector<int> col(m);
    for (int i = 0; i < m; i++) cin >> pat[i] >> w[i] >> col[i];

    // gains[x] : 位置 x 上每种颜色的「承诺长度 L -> 累计收益」
    // 同一位置同一颜色可以取多个模式;承诺长度取这些模式的最大长度,
    // 收益是所有长度不超过它的模式的收益之和。
    vector<vector<pair<int, vector<pair<int, long long>>>>> gains(n + 2);
    // raw[x] : (颜色, 长度) -> 该位置该颜色该长度的收益和
    vector<map<pair<int, int>, long long>> raw(n + 2);
    for (int i = 0; i < m; i++) {
        const string &t = pat[i];
        int len = (int)t.size();
        vector<int> pi(len, 0);
        for (int a = 1, j = 0; a < len; a++) {
            while (j > 0 && t[a] != t[j]) j = pi[j - 1];
            if (t[a] == t[j]) j++;
            pi[a] = j;
        }
        for (int x = 1, j = 0; x <= n; x++) {
            while (j > 0 && s[x - 1] != t[j]) j = pi[j - 1];
            if (s[x - 1] == t[j]) j++;
            if (j == len) {
                // 模式 i 在位置 x 处匹配结束,起点是 start = x-len+1:
                // 染颜色 col[i],范围 [start, x],收益 p[start]+w[i]
                int start = x - len + 1;
                raw[start][{col[i], len}] += p[start] + w[i];
                j = pi[j - 1];
            }
        }
    }
    // gains[x] : 位置 x 上每种颜色的「承诺长度 L -> 累计收益」
    // 同一位置同一颜色可以取多个模式;承诺长度取这些模式的最大长度,
    // 收益是所有长度不超过它的模式的收益之和。
    for (int x = 1; x <= n; x++) {
        map<int, vector<pair<int, long long>>> byColor;
        for (auto &e : raw[x]) byColor[e.first.first].push_back({e.first.second, e.second});
        for (auto &e : byColor) {
            sort(e.second.begin(), e.second.end());
            vector<pair<int, long long>> v;
            long long sum = 0;
            for (auto &f : e.second) { sum += f.second; v.push_back({f.first, sum}); }
            gains[x].push_back({e.first, v});
        }
    }

    // 状态:处理到位置 x,当前颜色 c(0 表示还没用过颜色),
    // y = 最近一次被染上 c 的位置,e = 当前颜色被「承诺」到的位置(<= y)。
    // dp[x*(m+1)+c] : key = y*(n+2)+e -> 最大收益
    const int W = m + 1;
    vector<map<long long, long long>> dp((size_t)(n + 2) * W);
    dp[(size_t)1 * W + 0][0] = 0;

    auto relax = [&](int x, int c, int y, int e, long long v) {
        if (x > n + 1) { x = n + 1; }
        auto &mp = dp[(size_t)x * W + c];
        long long key = (long long)y * (n + 2) + e;
        auto it = mp.find(key);
        if (it == mp.end() || it->second < v) mp[key] = v;
    };

    for (int x = 1; x <= n; x++) {
        for (int c = 0; c <= m; c++) {
            auto &mp = dp[(size_t)x * W + c];
            if (mp.empty()) continue;
            vector<pair<pair<int, int>, long long>> st;
            for (auto &e : mp) st.push_back({{(int)(e.first / (n + 2)), (int)(e.first % (n + 2))}, e.second});
            for (auto &s0 : st) {
                int y = s0.first.first, e = s0.first.second;
                long long v = s0.second;
                // 1) 位置 x 不染色(只有不在承诺区间里才可以)
                if (x > e) relax(x + 1, c, y, e, v);
                // 2) 用当前颜色 c 染色
                if (c > 0) {
                    relax(x + 1, c, max(y, x), e, v);       // 不取任何模式
                    for (auto &g : gains[x]) {
                        if (g.first != c) continue;
                        for (auto &f : g.second) {
                            relax(x + 1, c, max(y, x + f.first - 1), max(e, x + f.first - 1), v + f.second);
                        }
                    }
                }
                // 3) 换成另一种颜色
                for (int d = 1; d <= m; d++) {
                    if (d == c) continue;
                    if (c > 0 && x - y <= k) continue;      // 与最近的颜色 c 距离太近
                    if (x <= e) continue;                     // 承诺区间内不能换色
                    relax(x + 1, d, x, x, v);                 // 不取任何模式
                    for (auto &g : gains[x]) {
                        if (g.first != d) continue;
                        for (auto &f : g.second) {
                            relax(x + 1, d, x + f.first - 1, x + f.first - 1, v + f.second);
                        }
                    }
                }
            }
        }
    }

    long long ans = NEG;
    for (int c = 0; c <= m; c++) {
        for (auto &e : dp[(size_t)(n + 1) * W + c]) ans = max(ans, e.second);
    }
    cout << ans << '\n';
    return 0;
}

动画演示