• Luogu 3193 [HNOI2008]GT考试


    BZOJ1009

    妙!

    推荐这篇题解: https://www.luogu.org/blog/Edgration/solution-p3193

    考虑设计dp,设$f_{i, j}$表示长串匹配到i,短串匹配到j的方案数,初值有$f_{0,0} = 1$

        那么最后的答案   $ans = sum_{i = 0}^{m - 1} f_{n,i}$

    考虑转移,假设当前填到第i位,有一种填法能使$f_{i,j}$转移到$f_{i + 1, j + 1}$,那么填剩下的数字全部都转移到$f_{i + 1,0}$吗?

    错!这就是一开始想错的地方,填不一样的数字并不一定是转移到0的匹配位置,而是考虑转移到以j结尾的后缀的最长前缀!

    设$g_{i, j}$表示从i的匹配长度转移到j的匹配长度的方案数,有转移:

          $f_{i, j} = sum_{k = 0}^{m - 1}f_{i - 1, k} * g_{k, j}$

    因为给出的短串是恒定的,所以g数组的值也是恒定的,而找与后缀相匹配的最长前缀,肯定是想到kmp啦

    然而这样还是不足以通过本题,再次观察这个方程,发现这就是一个矩阵乘法的形式,相当于把f看成一个1*m的矩阵F,把g看成一个m*m的转移矩阵G。

          $F' = F * G^{n}$ 用G转移Fn次

    到此为止,本题全部解决,时间复杂度$O(m^{2}logn)$

    Code:

    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int N = 25;
    
    int n, m, P, nxt[N], mat[N][N];
    char str[N];
    
    inline void prework() {
        nxt[1] = nxt[0] = 0;
        for(int i = 2, j = 0; i <= m; i++) {
            for(; j > 0 && str[i] != str[j + 1]; j = nxt[j]);
            if(str[i] == str[j + 1]) j++;
            nxt[i] = j;
        }
        
        for(int i = 0; i < m; i++) {
            for(int j = '0'; j <= '9'; j++) {
                int tmp = i;
                for(; tmp > 0 && str[tmp + 1] != j; tmp = nxt[tmp]);
                if(str[tmp + 1] == j) tmp++;
                if(tmp < m) mat[i][tmp]++;
            }
        }
    }
    
    inline void work(int &x, int y) {
        x = (x + y % P) % P;
    }
    
    struct Matrix {
        int s[N][N];
        
        inline void init() {
            memset(s, 0, sizeof(s));
        }
        
        friend Matrix operator * (const Matrix &x, const Matrix &y) {
            Matrix res;
            res.init();
            for(int i = 0; i < m; i++)
                for(int j = 0; j < m; j++)
                    for(int k = 0; k < m; k++)
                        work(res.s[i][j], x.s[i][k] * y.s[k][j]);
            return res;
        }
        
        inline Matrix pow(int y) {
            Matrix res = *this, x = *this;
            for(y--; y > 0; y >>= 1) {
                if(y & 1) res = res * x;
                x = x * x;
            }
            return res;
        }
        
    } f, g;
    
    int main() {
        scanf("%d%d%d", &n, &m, &P);
        scanf("%s", str + 1);
        prework();
        
        for(int i = 0; i < m; i++)
            for(int j = 0; j < m; j++)
                g.s[i][j] = mat[i][j];
        g = g.pow(n);
        
        f.s[0][0] = 1; f = f * g;
        
        int ans = 0;
        for(int i = 0; i < m; i++)
            work(ans, f.s[0][i]);
        printf("%d
    ", ans);
        
        return 0;
    }
  • 相关阅读:
    SQLite常用SQL语句
    delphi设计浮动窗口
    在Delphi中使用键盘勾子获取键盘输入(译--5月7日)
    Delphi制作软键盘
    Delphi格式化输出函数(1): Format
    《你不知道的 CSS》之等比例缩放的盒子
    请用心练完这16个webpack小例子
    JavaScript高级内容笔记:原型链、继承、执行上下文、作用域链、闭包
    表格组件神器:bootstrap table详细使用指南
    玩转JavaScript正则表达式
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9474019.html
Copyright © 2020-2023  润新知