1009: [HNOI2008]GT考试

Time Limit: 1 Sec  Memory Limit: 162 MB
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

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
",ans);
60     return 0;
61 }
原文地址:https://www.cnblogs.com/InWILL/p/9236222.html