zoukankan      html  css  js  c++  java
  • 【题解】HNOI2008GT考试

    这题好难啊……完全不懂矩阵加速递推的我TAT

    这道题目要求我们求出不含不吉利数字的字符串总数,那么我们有dp方程 : dp[i][j](长度为 i 的字符串,最长与不吉利数字前缀相同的后缀长度为 j 的方案数)。 dp[i][j] = Σdp[i - 1][k] * a[k][j] (a 数组表示从 k 状态转移到 j 状态的方案数)。a 数组我们可以通过 kmp 对不吉利数字的每一个前缀后面加上‘0’~‘9’转移匹配得到(匹配成功表示成功转移状态,a[k][j]++;否则表示此时没有重合的后缀,a[k][0]++)。

    此时这道题目我们已经拥有了一个相对优的解法了,但是还不够。注意到上面的式子,我们对于dp数组与a数组分别建立矩阵,dp矩阵是一个列矩阵,一列代表1~k的状态,a矩阵第 j 行上每个数分别表示a[j][k]。所以得到的答案dp[i][j]即为dp矩阵与a矩阵第 j 行的乘积。矩阵快速幂优化即可。

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 100000
    int n, m, Mod, k, ans;
    char s[maxn], nxt[maxn];
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    struct Matrix
    {
        int num[52][52];
        void init()
        {
            memset(num, 0, sizeof(num));
        } 
        Matrix operator*(const Matrix &x)
        {
            Matrix tem;
            tem.init();
            for(int i = 0; i < m; i ++)
                for(int j = 0; j < m; j ++)
                    for(int k = 0; k < m; k ++)
                    {
                        tem.num[i][j] += (num[i][k] * x.num[k][j]) % Mod;
                        tem.num[i][j] %= Mod;
                    }
            return tem;
        }
    }T, S;
    
    void KMP()
    {
        int j = 0;
        for(int i = 2; i <= m; i ++) 
        {
            while(j && s[j + 1] != s[i]) j = nxt[j];
            if(s[j + 1] == s[i]) j ++;
            nxt[i] = j;
        }
        j = 0;
        for(int i = 0; i < m; i ++)
            for(int k = 0; k <= 9; k ++)
            {
                j = i;
                while(j && s[j + 1] != (char) k + '0') j = nxt[j];
                if(s[j + 1] == (char) k + '0') T.num[i][j + 1] ++;
                else T.num[i][0] ++;
            }
    }
    
    void Qpow()
    {
        for(int i = 0; i < m; i ++) S.num[i][i] = 1;
        while(n)
        {
            if(n & 1) S = S * T;
            T = T * T;
            n >>= 1;
        }
    }
    
    int main()
    {
        n = read(), m = read(), k = read();
        Mod = k;
        scanf("%s", s + 1);
        KMP();
        Qpow();
        for(int i = 0; i < m; i ++)
            ans = (ans + S.num[0][i]) % Mod;
        printf("%d
    ", ans);
        return 0;
    } 
  • 相关阅读:
    LeetCode 127. Word Ladder 单词接龙(C++/Java)
    LeetCode 681. Next Closest Time 最近时刻 / LintCode 862. 下一个最近的时间 (C++/Java)
    LeetCode 682. Baseball Game 棒球比赛(C++/Java)
    LeetCode 218. The Skyline Problem 天际线问题(C++/Java)
    小数据池,编码
    字典
    列表
    常见的数据类型
    while循环
    初始python
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/8542749.html
Copyright © 2011-2022 走看看