BZOJ1009: [HNOI2008]GT考试

1009: [HNOI2008]GT考试

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 4474  Solved: 2763
[Submit][Status][Discuss]

Description

  阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为
0

Input

  第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000

Output

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

Sample Input

4 3 100
111

Sample Output

81

HINT

Source

 

【题解】

不妨设dp[i][j]表示前i个字符,后缀有j个匹配上了,其余的没有匹配上

他们说做多了AC自动机上DP就能想出这个状态。。

可我还没写过AC自动机的题

预处理a[i][j]表示长度为i的前缀加上一个字符到达长度为j的前缀的方案数

转移为dp[i][j] = Σdp[i-1][k] * a[k][j]

很明显的矩阵乘法

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <cstdlib>
  5 #include <algorithm>
  6 #include <queue>
  7 #include <vector>
  8 #include <cmath> 
  9 #define min(a, b) ((a) < (b) ? (a) : (b))
 10 #define max(a, b) ((a) > (b) ? (a) : (b))
 11 #define abs(a) ((a) < 0 ? (-1 * (a)) : (a))
 12 inline void swap(int &a, int &b)
 13 {
 14     long long tmp = a;a = b;b = tmp;
 15 }
 16 inline void read(int &x)
 17 {
 18     x = 0;char ch = getchar(), c = ch;
 19     while(ch < '0' || ch > '9') c = ch, ch = getchar();
 20     while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
 21     if(c == '-') x = -x;
 22 }
 23 
 24 const int INF = 0x3f3f3f3f;
 25 const int MAXM = 30;
 26 
 27 int a[MAXM][MAXM], p[MAXM][MAXM], nxt[MAXM], k, n, m;
 28 char s[MAXM];
 29 
 30 void kmp()
 31 {
 32     nxt[0] = -1;
 33     for(register int i = 1, j = -1;i < m;++ i)
 34     {
 35         while(j >= 0 && s[i] != s[j + 1]) j = nxt[j];
 36         if(s[i] == s[j + 1]) ++ j;
 37         nxt[i] = j;
 38     }
 39     for(register int i = 0;i < m;++ i)
 40         for(register int j = '0';j <= '9';++ j)
 41         {
 42             int tmp = i - 1;
 43             while(tmp >= 0 && s[tmp + 1] != j) 
 44                 tmp = nxt[tmp];
 45             if(s[tmp + 1] == j) ++ a[i][tmp + 2];
 46             else ++ a[i][0]; 
 47         }
 48 }
 49 
 50 int tmp[MAXM][MAXM], base[MAXM][MAXM];
 51 
 52 void pow(int b)
 53 {
 54     for(register int i = 0;i < m;++ i)
 55         for(register int j = 0;j < m;++ j)
 56             base[i][j] = a[i][j];
 57     for(;b;b >>= 1)
 58     {
 59         if(b & 1)
 60         {
 61             memset(tmp, 0, sizeof(tmp));
 62             for(register int i = 0;i < m;++ i)
 63                 for(register int j = 0;j < m;++ j)
 64                     for(register int q = 0;q < m;++ q)
 65                     {
 66                         tmp[i][j] += a[i][q] * base[q][j] % k;
 67                         if(tmp[i][j] >= k) tmp[i][j] -= k;
 68                     }
 69             for(register int i = 0;i < m;++ i)
 70                 for(register int j = 0;j < m;++ j)
 71                     a[i][j] = tmp[i][j];
 72         }
 73         memset(tmp, 0, sizeof(tmp));
 74         for(register int i = 0;i < m;++ i)
 75             for(register int j = 0;j < m;++ j)
 76                 for(register int q = 0;q < m;++ q)
 77                 {
 78                     tmp[i][j] += base[i][q] * base[q][j] % k;
 79                     if(tmp[i][j] >= k) tmp[i][j] -= k;
 80                 }
 81         for(register int i = 0;i < m;++ i)
 82                 for(register int j = 0;j < m;++ j)
 83                     base[i][j] = tmp[i][j];
 84     } 
 85 } 
 86 
 87 int main()
 88 {
 89     read(n), read(m), read(k);scanf("%s", s);
 90     kmp();
 91     pow(n - 1);
 92     int sum = 0;
 93     for(register int i = 0;i < m;++ i) 
 94     { 
 95         sum += a[0][i];
 96         if(sum >= k) sum -= k;
 97     } 
 98     printf("%d", sum);
 99     return 0;
100 }
BZOJ1009

 

posted @ 2018-01-20 15:07  嘒彼小星  阅读(144)  评论(0编辑  收藏  举报