BZOJ 1009 [HNOI2008]GT考试

1009: [HNOI2008]GT考试

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 2154  Solved: 1327
[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位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6

Output

阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

Sample Input

4 3 100
111

Sample Output

81

HINT

 

Source

题解:做到这道题我很伤心。。。是ac(kmp)+dp

翻了这么多题解还是cjk讲的最好。。。请允许我转一下。。。

字符串上的动态规划:

按顺序处理准考证号每一位,
设f[i][j]表示:准考证号前i位中 后j位与不吉利数的前j位相同时,前i位的方案数 
那么答案ans=f[n][0]+f[n][1]+…+f[n][m-1]

f[i][j]的准确含义:
1.f[i][j]表示的每种方案不仅与其后j位有关,还应保证不含不吉利数 
2.为避免重复,f[i][j]表示的每种方案都不含长度大于j且与不吉利数的前缀相同 的后缀 
 否则就会出现:从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案 的情况 

状态转移:
f[i][j]只能由f[i-1][k]得到,相当于填完第i-1位后,将其后缀k(长为k的后缀)后面新添一位num,之后这个i位数的 与不吉利数前缀相同的最长后缀是:后缀j
i>=1时:f[i][j]=f[i-1][0]*a[0][j]+f[i-1][1]*a[1][j]+…+f[i-1][m-1]*a[m-1][j]
        比如:还是假设不吉利数为123124,那么 f[i][3]=f[i-1][2]+f[i-1][5],因为 f[i-1][2]末尾的*****12不能是**12312,所以需要f[i-1][5]补充 
        但若不吉利数为123123,那么 f[i][3]=f[i-1][2],因为 f[i][3]末尾的*****123不能是**123123
i==0时:f[0][0]=1,f[0][其他]=0
其中,a[k][j]就表示上面提到的num能取几个值,可以用kmp算法预处理出来,它是一个矩阵 

这样就可以不重不漏地计数了 

再来个矩阵加速:f[i][j]求法是个线性齐次递推式,可以构造成矩阵,然后加一下速。

这道题要留坑的。。。因为还不是很懂QAQ

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cstring>
 7 #define PAU putchar(' ')
 8 #define ENT putchar('\n')
 9 using namespace std;
10 const int maxn=30;
11 int n,mod,len,nxt[maxn];char str[maxn];
12 struct matrix{int f[maxn][maxn];matrix(){memset(f,0,sizeof(f));}}m,ans;
13 matrix operator*(const matrix&a,const matrix&b){
14     matrix c;
15     for(int i=0;i<len;i++)
16         for(int j=0;j<len;j++)
17             for(int k=0;k<len;k++)
18                 c.f[i][j]=(c.f[i][j]+a.f[i][k]*b.f[k][j])%mod;
19     return c;
20 }
21 void kmp(){
22     int j=0;nxt[1]=0;
23     for(int i=2;i<=len;i++){
24         while(j&&str[j+1]!=str[i]) j=nxt[j];
25         if(str[j+1]==str[i])j++;nxt[i]=j;
26     }return;
27 }
28 void dp(){
29     for(int i=0;i<len;i++)
30         for(int j=0;j<10;j++){
31             int k;
32             for(k=i;k;k=nxt[k]) if(str[k+1]-'0'==j) break;
33             if(str[k+1]-'0'==j) k++;
34             m.f[i][k]=(m.f[i][k]+1)%mod;
35         }
36     return;
37 }
38 void pow(){
39     for(int i=0;i<len;i++) ans.f[i][i]=1;
40     for(;n;n>>=1,m=m*m)if(n&1) ans=ans*m;return;
41 }
42 inline int read(){
43     int x=0,sig=1;char ch=getchar();
44     while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();}
45     while(isdigit(ch)) x=10*x+ch-'0',ch=getchar();
46     return x*sig;
47 }
48 inline void write(int x){
49     if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x;
50     int len=0,buf[15];while(x) buf[len++]=x%10,x/=10;
51     for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return;
52 }
53 int main(){
54     n=read();len=read();mod=read();scanf("%s",str+1);kmp();dp();pow();
55     int res=0;for(int i=0;i<len;i++) res=(res+ans.f[0][i])%mod;
56     write(res);
57     return 0;
58 }

 

你可能感兴趣的:(2008)