bzoj1009 [HNOI2008]GT考试
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
111
Sample Output
正解:$kmp$+矩阵快速幂加速。
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]求法是个线性齐次递推式,可以构造成矩阵,然后加一下速。
感觉自己还不是很理解。。慢慢推敲吧。。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 #include <cstdlib> 6 #include <cstdio> 7 #include <vector> 8 #include <cmath> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 #include <set> 13 #define inf (1<<30) 14 #define il inline 15 #define RG register 16 #define ll long long 17 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 18 19 using namespace std; 20 21 struct data{ int a[30][30]; }a,b; 22 23 int nxt[30],n,m,k,ans; 24 char s[30]; 25 26 il int gi(){ 27 RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 28 if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; 29 } 30 31 il data mul(RG data a,RG data b){ 32 RG data c; memset(c.a,0,sizeof(c.a)); 33 for (RG int i=0;i<m;++i) 34 for (RG int j=0;j<m;++j) 35 for (RG int p=0;p<m;++p){ 36 c.a[i][j]+=a.a[i][p]*b.a[p][j]%k; 37 if (c.a[i][j]>=k) c.a[i][j]-=k; 38 } 39 return c; 40 } 41 42 il void work(){ 43 n=gi(),m=gi(),k=gi(); scanf("%s",s+1); 44 for (RG int i=2;i<=m;++i){ 45 RG int j=nxt[i-1]; 46 while (j && s[j+1]!=s[i]) j=nxt[j]; 47 if (s[j+1]==s[i]) nxt[i]=j+1; 48 } 49 for (RG int i=0;i<m;++i) 50 for (RG int j=0;j<=9;++j){ 51 RG int t=i; while (t && s[t+1]!=j+'0') t=nxt[t]; if (s[t+1]==j+'0') t++; 52 if (t!=m){ b.a[i][t]++; if (b.a[i][t]>=k) b.a[i][t]-=k; } 53 } 54 for (RG int i=0;i<m;++i) a.a[i][i]=1; 55 while (n){ if (n&1) a=mul(a,b); b=mul(b,b); n>>=1; } 56 for (RG int i=0;i<m;++i){ ans+=a.a[0][i]; if (ans>=k) ans-=k; } 57 printf("%d\n",ans); return; 58 } 59 60 int main(){ 61 File("gt"); 62 work(); 63 return 0; 64 }