BZOJ 5325 [Jsoi2017]码农 题解

有一个很显然的 DP, 设 f[i] 表示前 i 考虑完了前 i 个字符的最小代价.

有两种转移:

  • f[i]=f[i1]+c[si].
  • f[i]=mindi1j<i{f[j]+calc(j+1,i)}.

di 为最小的满足 ([di,i][1,di1] 的子串) 的位置, 可以用 SAM 预处理出来.
具体来说, 将字符 si 依次加入, 维护一个指针表示当前最大的 p 使得 [i+1,p][1,i] 的子串.

然后发现第 2 种转移可以用单调队列优化, 就完了. 复杂度为 O(N).

</:>

#include <bits/stdc++.h>
#define N 202200
using namespace std;
int sze, las, lnk[N << 1], len[N << 1], nxt[N << 1][10];
 
void extend(int c) {
    int cur = ++sze, p; cur[len] = las[len] + 1;
    for (p = las; p && !nxt[p][c]; p = lnk[p])
        nxt[p][c] = cur;
    if (!p) lnk[cur] = 1;
    else {
        int q = nxt[p][c];
        if (len[q] == len[p] + 1) lnk[cur] = q;
        else {
            int cln = ++sze; cln[len] = p[len] + 1;
            memcpy(nxt[cln], nxt[q], sizeof nxt[q]);
            lnk[cln] = lnk[q], lnk[q] = lnk[cur] = cln;
            for (; p && nxt[p][c] == q; p = lnk[p])
                nxt[p][c] = cln;
        }
    } las = cur;
}
 
int n, a, b, c[12], d[N];
long long f[N];
char s[N];
 
inline int go(int p, int ch, int le) {
    while (p != 1 && !nxt[p][ch])
        p = lnk[p], le = len[p];
    if (nxt[p][ch]) p = nxt[p][ch], ++le;
    return le;
}
 
int main() {
    scanf("%s", s + 1);
    n = strlen(s + 1);
    for (int i = 0; i <= 9; ++i)
        scanf("%d", c + i);
    cin >> a >> b;
 
    las = sze = 1;
    int p = 1, t = 0, le = 0;
    for (int i = 1, j = 1; i <= n; ++i) {
        extend(s[i] - '0');
        while (t < n && go(p, s[t + 1] - '0', le) >= t + 1 - i) {
            int ch = s[++t] - '0';
            while (p != 1 && !nxt[p][ch])
                le = len[p = lnk[p]];
            if (nxt[p][ch]) p = nxt[p][ch], ++le;
        }
        while (j <= t) d[j++] = i + 1;
    }
 
    static int q[N], ql = 1, qr = 0;
    for (int i = 1; i <= n; ++i) {
        f[i] = f[i - 1] + c[s[i] - '0'];
        while (ql <= qr && q[ql] < d[i] - 1) ++ql;
        if (ql <= qr) {
            int j = q[ql];
            f[i] = min(f[i], f[j] + 1ll * a * (i - j) + b);
        }
        while (ql <= qr && (f[i] - 1ll * a * i) <= (f[q[qr]] - 1ll * a * q[qr])) --qr;
        q[++qr] = i;
    }
 
    cout << f[n] << endl;
 
    return 0;
}
posted @   Jerx2y  阅读(211)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示