【题解】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\n", ans);
    return 0;
} 

 

posted @ 2018-03-11 23:11  Twilight_Sx  阅读(208)  评论(0编辑  收藏  举报