bzoj 1009

[HNOI2008]GT考试

Description

阿申准备报名参加GT考试, 准考证号为 NN 位数 X1X2Xn(0Xi9)X_1X_2\dots{}X_n(0 \le X_i \le 9), 他不希望准考证号上出现不吉利的数字。 他的不吉利数学 A1A2Am(0Ai9)A_1A_2\dots{}A_m(0\le A_i \le 9)MM 位, 不出现是指 X1X2XnX_1X_2 \dots X_n 中没有恰好一段等于 A1A2AmA_1A_2 \dots A_m. A1A_1X1X_1 可以为 00.

Input

第一行输入 N,M,KN,M,K. 接下来一行输入 MM 位的数. N109,M20,K1000N \le 10^9, M \le 20, K \le 1000.

Output

阿申想知道不出现不吉利数字的号码有多少种,输出模 KK 取余的结果.

Sample Input

4 3 100
111

Sample Output

81

题解

f[i][j]f[i][j] 表示考虑到第 ii 位,其后缀最大能匹配到不吉利数字前面 j(0j<M)j(0 \le j < M) 位的方案数. 则有

f[i][j]=l=0M1ajlf[i1][l],0j<Mf[i][j] = \sum_{l = 0}^{M - 1}a_{jl}f[i-1][l], \quad 0 \le j < M

式中 ajla_{jl} 的意义是 AlA_l 加上一个字符后其后缀是 AjA_j 的方案数.

边界条件 f[0][0]=1,f[0][1..(M1)]=0f[0][0] = 1, f[0][1..(M-1)] = 0.

显然这可以用矩阵快速幂加速,最后答案就是 i=0M1f[N][i]\sum_{i=0}^{M-1}f[N][i].

现在考虑怎么求出系数 ajia_{ji}. 初始令所有的 aji=0a_{ji} = 0. 我们用 KMP 处理出串 AA 的失配函数 p[1..M]p[1..M],遍历每一位 AiA_i, 枚举添加的字符 cc,然后令 j=ij = i,如果 Aj+1cA_{j+1} \ne c, 则令 j=p[j]j = p[j] 直到 Aj+1=cA_{j+1} = c 或者 j=0j = 0. 接着如果 Aj+1=cA_{j+1} = c, 则表示在 A[1..i]A[1..i] 后添加一个字符 cc 的话, A[1..(j+1)]A[1..(j+1)] 是这个串的一个后缀,且是最长的一个是 AA 的一个前缀的后缀, 也就是说状态 f[s1][i]f[s-1][i] 可以转移到 f[s][j+1]f[s][j+1],于是我们令 a(j+1)ia_{(j+1)i} 加上 11. 否则必有 j=0j = 0,就是说 A[1..i]A[1..i] 添加字符 cc 后, 没有后缀是 AA 的一个前缀,那么状态 f[s1][i]f[s-1][i] 也就转移到了 f[s][0]f[s][0], 令 a0ia_{0i}11 即可.

代码

#include <cstdio>

using namespace std;

int k;
inline void add(int &dest, int delta)
{
    dest += delta;
    if (dest < 0) dest += k;
    if (dest >= k) dest -= k;
}
inline void mul(int &dest, int times)
{
    dest *= times;
    dest -= dest / k * k;
}
inline int rmul(int a, int b)
{
    int z = a * b;
    return z - z / k * k;
}

int M;

#define MATSZ (1 << 5)
/* dest = b * dest */
void matmul(int dest[MATSZ][MATSZ], int b[MATSZ][MATSZ])
{
    int tmp[MATSZ][MATSZ] = {{0}};
    for (int i = 0; i < M; i++)
        for (int j = 0; j < M; j++)
            for (int l = 0; l < M; l++)
                add(tmp[i][j], rmul(b[i][l], dest[l][j]));
    for (int i = 0; i < M; i++)
        for (int j = 0; j < M; j++)
            dest[i][j] = tmp[i][j];
}

char A[MATSZ];
int f[MATSZ];
int trans[MATSZ][MATSZ];

void calc_transMAT(void)
{
    int i, j;
    f[1] = j = 0;
    for (i = 2; i <= M; i++)
    {
        while (j  && A[i] != A[j + 1]) j = f[j];
        if (A[i] == A[j + 1]) j++;
        f[i] = j;
    }
    for (i = 0; i < M; i++)
        for (j = '0'; j <= '9'; j++)
        {
            int l = i;
            while (l && A[l + 1] != j) l = f[l];
            if (A[l + 1] == j) l++;
            /* i -> l is possible */
            if (l < M) add(trans[l][i], 1);
        }
}

int dp[MATSZ][MATSZ];

int main(void)
{
    int n;
    scanf("%d%d%d", &n, &M, &k);
    scanf("%s", A + 1);
    calc_transMAT();
    dp[0][0] = 1;
    while (n)
    {
        if (1 & n) matmul(dp, trans);
        matmul(trans, trans);
        n >>= 1;
    }
    int ans = 0;
    for (int i = 0; i < M; i++)
        add(ans, dp[i][0]);
    printf("%d\n", ans);
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Last Updated: 8/21/2018, 3:23:17 PM