bzoj 1009 [HNOI2008]GT考试 矩阵乘法+KMP

题面

题目传送门

解法

用KMP的思想来转移dp,并用矩乘优化

  • 很明显,我们可以这样设计状态:设 f [ i ] [ j ] 表示现在已经填到第 i 位,且与不吉利数字已经匹配了 j 位的方案数。最后的答案显然为 f [ n ] [ i ]   ( i < m )
  • 考虑如何转移。求出 f [ i ] [ j ] 的前驱节点似乎并没有那么方便,考虑通过 f [ i ] [ j ] 来推后面的节点
  • 如果下一位匹配,那么 f [ i ] [ j ] 可以转移到 f [ i + 1 ] [ j + 1 ] 。如果下一位失配,我们考虑下一位是一个什么字符,假设 f [ i ] [ j ] 可以转移到 f [ i + 1 ] [ n x t [ j ] [ k ] ] 。其中, n x t [ j ] [ k ] 表示当前匹配到第 j 位,且在第 j + 1 位失配,第 j + 1 位的字符为 k 的最长前缀=后缀的长度,这个和KMP中 n x t 数组的定义几乎是一样的
  • n x t [ j ] [ k ] 直接暴力求就可以了,如果想提升代码速度可以写一个KMP
  • 然而,现在的复杂度最好为 O ( n m ) n 10 9 ,考虑如何优化
  • 我们发现,每一次转移的时候, j 可以转移到的位置其实和 i 并没有什么关系,所以我们可以直接对 i 这一位矩乘,然后就能通过此题啦
  • 时间复杂度: O ( m 3 log n )

代码

#include <bits/stdc++.h>
#define N 40
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Matrix {
    int a[N][N];
    void Clear() {memset(a, 0, sizeof(a));}
};
int n, m, p;
Matrix operator * (Matrix x, Matrix y) {
    Matrix ret; ret.Clear();
    for (int k = 0; k < m; k++)
        for (int i = 0; i < m; i++)
            for (int j = 0; j < m; j++)
                ret.a[i][j] = (ret.a[i][j] + x.a[i][k] * y.a[k][j] % p) % p;
    return ret;
}
Matrix operator ^ (Matrix x, int y) {
    Matrix ret = x; y--;
    while (y) {
        if (y & 1) ret = ret * x;
        y >>= 1, x = x * x;
    }
    return ret;
}
int calc(string st) {
    string x = "", y = ""; int ret = 0;
    for (int l = 0, r = st.size() - 1; l < st.size(); l++, r--) {
        x = x + st[l], y = st[r] + y;
        if (x == y && l < st.size() - 1) ret = l + 1;
    }
    return ret;
}
int main() {
    read(n), read(m), read(p);
    string st, tmp = ""; cin >> st;
    Matrix tx; tx.Clear();
    for (int i = 0; i < m; i++) {
        tx.a[i][i + 1] = 1;
        for (char j = '0'; j <= '9'; j++) {
            if (j == st[i]) continue;
            tx.a[i][calc(tmp + j)]++;
        }
        tmp = tmp + st[i];
    }
    tx = tx ^ n;
    Matrix ty; ty.Clear(); ty.a[0][0] = 1;
    ty = ty * tx; int ans = 0;
    for (int i = 0; i < m; i++) ans = (ans + ty.a[0][i]) % p;
    cout << ans << "\n";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/82084791