• P3193 [HNOI2008]GT考试(kmp自动机+dp+矩阵加速)


    题目传送门(luogu)

    看到计数,容易想到dp。

    像这种字符串匹配有关的dp一般都有一维是自动机上的状态(套路)。因为要构建长度为n的准考证号所以还有一维长度。

    这样我们就知道了子状态:$dp[i][j]$代表长度为i的准考证号与不吉利数字匹配长度为j的方案数。

    很容易想到转移方程:$dp[i][j]=sum_{k=0}^{m-1}dp[i-1][k] imes g[k][j]$。其中$g[k][j]$代表加一个字符从状态k到状态j的方案数。

    先来看怎么求这个g,这就要用到kmp自动机

    枚举第一个状态i,然后再枚举在后面加的字符c,如果kmp自动机已经建出来了则可以知道加一个字符可以从i到j,令$g[i][j]++$。

    ch[0][s[1] - '0'] = 1;
    for (int i = 0; i < m; ++i) {
        for (char k = '0'; k <= '9'; ++k) {
            if (k == s[i + 1]) ch[i][k - '0'] = i + 1;
            else ch[i][k - '0'] = ch[nxt[i]][k - '0'];
            g.arr[i][ch[i][k - '0']]++;
        }
    }

    如果暴力去跑刚才的dp的复杂度是$O(nm^2)$的,肯定会炸。

    我们发现每次转移都只和i-1和g有关,而g有是不变的,所以可以用矩阵加速转移。

    /*
    dp[i][j]=dp[i-1][k] * g[k][j]
    */
    #include <bits/stdc++.h>
    using namespace std;
    struct Matrix{
        int arr[21][21], size;
    }g, dp;
    int n, m, mod;
    char s[21];
    int nxt[21];
    int ans;
    int ch[21][10];
    void init(Matrix &ret, int size) {
        ret.size = size;
        for (int i = 0; i <= size; i++) {
            for (int j = 0; j <= size; j++) {
                ret.arr[i][j] = 0;
            }
        }
    }
    Matrix Mul(Matrix A, Matrix B) {
        Matrix C;
        init(C, A.size);
        for (int i = 0; i <= A.size; i++) {
            for (int j = 0; j <= A.size; j++) {
                for (int k = 0; k <= A.size; k++) {
                    C.arr[i][j] = (C.arr[i][j] + (A.arr[i][k] * B.arr[k][j]) % mod) % mod;
                }
            }
        }
        return C;
    }
    Matrix ksm(Matrix A, int b) {
        Matrix ret;
        init(ret, A.size);
        for (int i = 0; i <= ret.size; i++) ret.arr[i][i] = 1;
        while (b) {
            if (b & 1) ret = Mul(ret, A);
            A = Mul(A, A);
            b >>= 1;
        }
        return ret;
    }
    int main() {
        scanf("%d%d%d", &n, &m, &mod);
        scanf("%s", s + 1);
        for (int i = 2, j = 0; i <= m; i++) {
            while (j && s[j + 1] != s[i]) j = nxt[j];
            if (s[j + 1] == s[i]) j++;
            nxt[i] = j;
        }
        init(g, m - 1);
        ch[0][s[1] - '0'] = 1;
        for (int i = 0; i < m; ++i) {
            for (char k = '0'; k <= '9'; ++k) {
                else ch[i][k - '0'] = ch[nxt[i]][k - '0'];
                g.arr[i][ch[i][k - '0']]++;
            }
        }
        
        init(dp, m - 1);
        dp.arr[0][0] = 1;
        dp = Mul(dp, ksm(g, n));
        for (int i = 0; i < m; i++) {
            ans = (ans + dp.arr[0][i]) % mod;
        }
        printf("%d", ans);
        return 0;
    }
  • 相关阅读:
    第十三周时间进度表
    第十二周时间进度表
    第十周时间进度表
    elasticsearch(ES)日志迁移
    docker stack 部署nginx
    docker stack 部署容器监控方案(cAdvisor、Prometheus、Grafana)
    docker stack 部署 mysql 5.6
    docker stack 部署 filebeat
    docker stack 部署 redis
    docker stack 部署 seafile(http)
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/13054924.html
Copyright © 2020-2023  润新知