Tang-Dreamoon's Blog

bzoj 1009 GT考试

DP+矩阵快速幂优化

  题目链接:bzoj 1009

  题意:写 B z o j 的题意就是简单——题意见上……

  题解:不出现不吉利数字即意味着在大串 N 中没出现过小串 M 。我们考虑在 K M P 上 D P ,即一边 D P 一边跑 K M P。我们用 F[i][j] 表示在大串中走到第 i 位在小串中走到第 j 位的方案数。然后我们枚举下一位的数根据 KMP 可以转移到 F[i+1][k],但是直接 DP 的话复杂度是 O(N*M) 的我们无法接受,所以我们用矩阵快速幂优化一下就好了。

  我思故我在:对于已知的、固定的、较少的转移,我们都可以用矩阵快速幂优化 DP 。

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
85
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,modd;
char ap[23];
int next[233];
struct table{
int c[23][23];
friend table operator * (const table &a,const table &b)
{
table lala;
memset(lala.c,0,sizeof(lala.c));
for(int i=0;i<=m-1;i++)
{
for(int j=0;j<=m-1;j++)
{
for(int k=0;k<=m-1;k++)
{
lala.c[i][j]=(lala.c[i][j]+a.c[i][k]*b.c[k][j]%modd)%modd;
}
}
}
return lala;
}
};
void work()
{
next[1]=0;
int j=0,lena=strlen(ap+1);
for(int i=2;i<=lena;i++)
{
while(j&&ap[j+1]!=ap[i]) j=next[j];
if(ap[j+1]==ap[i]) j++;
next[i]=j;
}
}
table kmi(table &a,int b)
{
table now=a,ret;
for(int i=0;i<=m-1;i++)
{
ret.c[i][i]=1;
}
while(b)
{
if(b&1) ret=ret*now;
now=now*now;
b>>=1;
}
return ret;
}
int main()
{
scanf("%d%d%d",&n,&m,&modd);
scanf("%s",ap+1);
work();
table map,ans;
memset(map.c,0,sizeof(map.c));
memset(ans.c,0,sizeof(ans.c));
for(int i=0;i<=m-1;i++)
{
for(int j=0;j<=9;j++)
{
int k=i;
while(k&&(ap[k+1]-'0')!=j) k=next[k];
if((ap[k+1]-'0')==j) k++;
// cout<<i<<" "<<j<<" "<<k<<endl;
if(k!=m)
map.c[i][k]++;
}
}
map=kmi(map,n);
ans.c[0][0]=1;
ans=ans*map;
int tot=0;
for(int i=0;i<=m-1;i++)
{
tot=(tot+ans.c[0][i])%modd;
}
cout<<tot<<endl;
return 0;
}