[HNOI2008]GT考试
题目描述
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0
输入输出格式
输入格式:
第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000
输出格式:
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
输入输出样例
输入样例#1:
4 3 100 111
输出样例#1:
81
设f[i][j]表示:准考证号前i位中 后j位与不吉利数的前j位相同时的方案数
那么答案ans=f[n][0]+f[n][1]+…+f[n][m-1]
为避免重复,f[i][j]表示的每种方案都不含长度大于j且与不吉利数的前缀相同的后缀
否则就会出现:
从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案的情况
转移:f[i][j]=∑f[i-1][k]*a[k][j]
a[k][j]为字符串中前缀1~k变成前缀1~j的方案数
a数组用kmp预处理,在求出next后,对于每一个前缀1~i,在后面加入'0'~'9',进行匹配
对于匹配的结果j,则a[i][j]++,匹配失败则f[i][0]++
算法变成了O(n)还不够
我们发现转移方程可以写成矩阵
这样就可以用矩阵快速幂
复杂度O(logn*m^3)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int Mod; 7 int n,m,nxt[51]; 8 int ans; 9 char s[51]; 10 struct Matrix 11 { 12 int num[51][51]; 13 Matrix init() 14 { 15 memset(num,0,sizeof(num)); 16 } 17 Matrix operator*(const Matrix &x) 18 { 19 Matrix c; 20 c.init(); 21 int i,j,k; 22 for (i=0;i<m;i++) 23 { 24 for (j=0;j<m;j++) 25 { 26 for (k=0;k<m;k++) 27 { 28 c.num[i][j]+=(num[i][k]*x.num[k][j])%Mod; 29 c.num[i][j]%=Mod; 30 } 31 } 32 } 33 return c; 34 } 35 }a[51][51],S,T; 36 void KMP() 37 {int i,j,k; 38 j=0; 39 nxt[1]=0; 40 for (i=2;i<=m;i++) 41 { 42 while (j&&s[j+1]!=s[i]) j=nxt[j]; 43 if (s[j+1]==s[i]) j++; 44 nxt[i]=j; 45 } 46 j=0; 47 for (i=0;i<m;i++) 48 { 49 for (k=0;k<=9;k++) 50 { 51 j=i; 52 while (j&&s[j+1]!=(char)(k+'0')) j=nxt[j]; 53 if (s[j+1]==k+'0') T.num[i][j+1]++; 54 else T.num[i][0]++; 55 } 56 } 57 } 58 void qpow() 59 {int i,j; 60 for (i=0;i<m;i++) 61 S.num[i][i]=1; 62 while (n) 63 { 64 if (n&1) 65 { 66 S=S*T; 67 } 68 T=T*T; 69 n>>=1; 70 } 71 } 72 int main() 73 {int k,i,j; 74 char ch; 75 cin>>n>>m>>k; 76 Mod=k; 77 scanf("%s",s+1); 78 KMP(); 79 qpow(); 80 for (i=0;i<m;i++) 81 ans=(ans+S.num[0][i])%Mod; 82 cout<<ans; 83 }