[BZOJ1009]GT考试

1009: [HNOI2008]GT考试

Time Limit: 1 Sec  Memory Limit: 162 MB

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

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 }
BZOJ1009

转载于:https://www.cnblogs.com/LadyLex/p/7061005.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值