• BZOJ 5325 [Jsoi2017]码农 题解


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

    有两种转移:

    • (f[i]=f[i-1]+c[s_i]).
    • (f[i]=min_{d_i-1le j<i}{f[j]+calc(j+1,i)}).

    (d_i) 为最小的满足 (([d_i,i])([1,d_{i}-1]) 的子串) 的位置, 可以用 SAM 预处理出来.
    具体来说, 将字符 (s_i) 依次加入, 维护一个指针表示当前最大的 (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;
    }
    
  • 相关阅读:
    Python学习第61天(html之form标签)
    Python学习第60天(html之body标签)
    Python学习第59天(web前端html /1))
    Python学习第58天(selector版本的ftp习题实现)
    Python学习第57天(异步IO)
    Python学习第56天(configpraser模块复习)
    Python学习第55天(IO多路复用)
    Python学习第54天(阻塞(blocking) IO和非阻塞(non-blocking)IO)
    如何通过Git Bash的命令行将电脑本地项目上传到自己的GitHub上
    第10周周博客
  • 原文地址:https://www.cnblogs.com/hnfms-jerry/p/solution_bzoj5325.html
Copyright © 2020-2023  润新知