bzoj1009 [ HNOI2008 ] -- KMP+矩阵乘法加速DP

令f[i][j]表示前i个字符,匹配到不吉利数字的第j位的方案数。

枚举第i+1位,通过KMP求出前i+1个字符可以匹配到不吉利数字的第几位,递推。

但由于n<=109,要用矩阵乘法加速。

f[i][j]=a[j][0]*f[i-1][0]+a[j][1]*f[i-1][1]+...+a[j][m-1]*f[i-1][m-1]

那么f[n]就是 an×f[0]

用快速幂,时间复杂度为O(log2n*m3)

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define N 30
 6 struct J{
 7     int a[N][N];
 8 }a,b;
 9 int Ans,f[N],i,j,k,n,m,x,y;
10 char s[N];
11 inline J Ch(J a,J b){
12     J c;
13     for(int i=0;i<m;i++)
14     for(int j=0;j<m;j++){
15         c.a[i][j]=0;
16         for(int p=0;p<m;p++)
17         c.a[i][j]=(c.a[i][j]+a.a[i][p]*b.a[p][j])%k;
18     }
19     return c;
20 }
21 inline J Pow(J a,int y){
22     if(y==1)return a;
23     J c=Pow(a,y>>1);
24     c=Ch(c,c);
25     if(y&1)c=Ch(a,c);
26     return c;
27 }
28 int main()
29 {
30     scanf("%d%d%d%s",&n,&m,&k,s+1);
31     for(i=1;i<=m;i++)s[i]-=48;
32     for(f[1]=f[i=2]=1;i<m;i++){
33         j=f[i];
34         while(j>1&&s[j]!=s[i])j=f[j];
35         f[i+1]=s[j]==s[i]?j+1:1;
36     }
37     a.a[0][0]=1;
38     for(i=0;i<m;i++)
39     for(j=0;j<=9;j++){
40         for(x=i+1;x>1&&j!=s[x];x=f[x]);
41         if(j!=s[x])x=0;
42         if(x<m)b.a[i][x]=(b.a[i][x]+1)%k;
43     }
44     a=Ch(a,Pow(b,n));
45     for(i=0;i<m;i++)Ans=(Ans+a.a[0][i])%k;
46     printf("%d",Ans);
47     return 0;
48 }
bzoj1009

 

posted @ 2017-03-16 10:22  gjghfd  阅读(244)  评论(0编辑  收藏  举报