1009: [HNOI2008]GT考试
Submit: 4779 Solved: 2977
[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
111
Sample Output
81
最近被填志愿弄得很烦,家长各种建议→_→,学校那边三天一小会,五天一大会。。。嘛,成都再见(。・_・)/~~~
本题是一道DP+矩阵乘法,推理过程如下:
首先我们可以设一个数组f[i][j],i表示准考证号匹配到了第i位,j表示不吉利数字匹配到了第j位,f[i][j]表示有多少种符合条件的号码
因此可以看出f[i+1][k]取决于f[i][j]后下一位填的数字
以范例为例,假设:准考证匹配到了第2位,不吉利号码匹配到了第1位,那么f[3][0]=f[2][0]*9+f[2][1]*9+f[2][2]*0,f[3][1]=f[2][0]*1+f[2][1]*0+f[2][2]*0
因为如果下一位不填1的情况有9种,而不填1都会导致不吉利号码匹配到0位因此有九种情况
因此可以得出公式:
\[f[i][j]=\sum_{k=0}^{m-1} f[i-1][k]*g[k][j]\]
g[i][j]代表从第j位匹配到第i位能填几种数字,因此我们只要求出g[i][j]就完成了最难的部分,这部分可以通过kmp+枚举来求
但是直接for循环10^9的数量级无疑会TLE,因此这里需要使用矩阵加速
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 char ch[30]; 7 int p[30],g[30][30],f[30][30]; 8 int n,m,mod,ans; 9 10 void initnext(char *t,int *p) 11 { 12 int j=0; 13 for(int i=1;i<m;i++) 14 { 15 while(j&&t[i]!=t[j]) j=p[j]; 16 if(t[i]==t[j]) j++; 17 p[i+1]=j; 18 } 19 } 20 21 void mult(int a[30][30],int b[30][30],int ans[30][30]) 22 { 23 int tmp[30][30]; 24 memset(tmp,0,sizeof(tmp)); 25 for(int i=0;i<m;i++) 26 for(int j=0;j<m;j++) 27 for(int k=0;k<m;k++) 28 tmp[i][j]=(tmp[i][j]+a[i][k]*b[k][j])%mod; 29 for(int i=0;i<m;i++) 30 for(int j=0;j<m;j++) 31 ans[i][j]=tmp[i][j]; 32 } 33 34 int main() 35 { 36 scanf("%d %d %d",&n,&m,&mod); 37 scanf("%s",ch); 38 initnext(ch,p);//求出前缀数组 39 for(int i=0;i<m;i++)//转移计算 40 for(int j=0;j<=9;j++) 41 { 42 int t=i; 43 while(t>0&&ch[t]-'0'!=j) 44 t=p[t]; 45 if(ch[t]-'0'==j) t++; 46 if(t!=m) g[t][i]=(g[t][i]+1)%mod; 47 } 48 for(int i=0;i<m;i++) 49 f[i][i]=1; 50 51 while(n) 52 { 53 if(n&1) mult(f,g,f); 54 mult(g,g,g); 55 n>>=1; 56 } 57 for(int i=0;i<m;i++) 58 ans=(ans+f[i][0])%mod; 59 printf("%d\n",ans); 60 return 0; 61 }