1009: [HNOI2008]GT考试
Time Limit: 1 Sec Memory Limit: 162 MBDescription
阿申准备报名参加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
题解:
KMP套上dp还真是第一次见……想的时候想歪了
f[i][j]为前i位准考证,后j位和不吉利数字的前j位匹配时前i位的匹配方案数,那么ans=∑f[n][i],i∈[0,m-1].
先用kmp跑出fail数组
为了便于处理,f[i][j]同时满足不重复,即f[i][fail[j]]中不包含f[i][j]的方案
比如对于不吉利数字为123124的情况,f[i][2](*****12)中就不能包含f[i][5](**12312)的方案
考虑转移,显然f[i][j]只能由f[i-1][k]转移过来
考虑对于f[i-1][k]的后k位,那么我们可以枚举填上的每一种可能数字(0到9),看填上之后会转移到f[i][j]的哪一个j,这里通过前面的fail数组跳跃
还是对于123124这个例子,容易看出(手玩一下)f[i][3]=f[i-1][2](变成***123)+f[i-1][5](变成***123123)
意思就是匹配不吉利数字前3位可以通过匹配前两位填上3转移过来,也可以通过前五位填上3转移过来
同时,填上数字之后不能非法,比如不吉利数字为123123,f[i][3]=f[i-1][2](变成***123),而没有再加上f[i][5](变成***123123)
初始时,f[0][0]=1。
然后我们考虑,f[i][j]的式子是一个线性递推式,我们就可以打个矩阵乘法优化了,这里不再赘述方法
代码见下:
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 using namespace std; 5 int A[25][25],B[25][25],tmp[25][25]; 6 int n,m,k,fail[25];char s[25]; 7 inline void quick_mod(int mi) 8 { 9 for(int i=0;i<25;i++)B[i][i]=1; 10 while(mi) 11 { 12 if(mi&1) 13 { 14 memset(tmp,0,sizeof(tmp)); 15 for(int i=1;i<=m;i++) 16 for(int j=1;j<=m;j++) 17 for(int u=1;u<=m;u++) 18 tmp[i][j]=(tmp[i][j]+B[i][u]*A[u][j])%k; 19 for(int i=1;i<=m;i++) 20 for(int j=1;j<=m;j++) 21 B[i][j]=tmp[i][j]; 22 } 23 mi>>=1; 24 memset(tmp,0,sizeof(tmp)); 25 for(int i=1;i<=m;i++) 26 for(int j=1;j<=m;j++) 27 for(int u=1;u<=m;u++) 28 tmp[i][j]=(tmp[i][j]+A[i][u]*A[u][j])%k; 29 for(int i=1;i<=m;i++) 30 for(int j=1;j<=m;j++) 31 A[i][j]=tmp[i][j]; 32 } 33 } 34 inline void get_fail() 35 { 36 for(int i=1,j=fail[i];i<m;i++,j=fail[i]) 37 { 38 while(j&&s[i]!=s[j])j=fail[j]; 39 fail[i+1]=(s[i]==s[j])?j+1:0; 40 } 41 for(int i=0;i<m;i++) 42 for(int j='0';j<='9';j++) 43 { 44 int u=i; 45 while(u&&s[u]!=j)u=fail[u]; 46 if(s[u]==j)u++; 47 if(u!=m)A[i+1][u+1]=(A[i+1][u+1]+1)%k; 48 } 49 } 50 int main() 51 { 52 scanf("%d%d%d",&n,&m,&k); 53 scanf("%s",s);int ans=0; 54 get_fail();quick_mod(n); 55 for(int i=1;i<=m;i++)ans=(ans+B[1][i])%k; 56 printf("%d",ans); 57 }