BZOJ1009 [HNOI2008]GT考试
KMP+矩阵快速幂+DP;
按顺序处理准考证号每一位,
设f[i][j]表示:准考证号前i位中 后j位与不吉利数的前j位相同时,前i位的方案数
那么答案ans=f[n][0]+f[n][1]+…+f[n][m-1]
状态转移:
f[i][j]只能由f[i-1][k]得到,相当于填完第i-1位后,将其后缀k(长为k的后缀)后面新添一位num,之后这个i位数的 与不吉利数前缀相同的最长后缀是j
转移方程: f[i][j] = f[i-1][0]*pre[0][j] + f[i-1][1]*pre[1][j] + f[i-j][2]*pre[2][j] + ...... f[i-1][m-1]*pre[m-1][j];(没有f[i-1][m]是因为这种情况是不合法的)
那么怎么求出这样的处理呢? 使用kmp的next数组,从i位置开始,然后开始枚举第i+1位的数字开始向后匹配,然后找到可以匹配的最大的那个位置,基本写法参照kmp的匹配。每找到某一个位置,把那个位置的+1即可。
因为我们匹配中尽量让不吉利的串长,所以匹配最大的那个位置(熟悉next数组应该很容易理解)
观察式子是个矩阵快速幂,可以log求出答案。
By:大奕哥
1 #include<bits/stdc++.h> 2 using namespace std; 3 int nex[25];int n,m,mod; 4 void setnex(char s[],int n) 5 { 6 int j=0;nex[1]=j; 7 for(int i=2;i<=n;++i) 8 { 9 while(j&&s[i]!=s[j+1])j=nex[j]; 10 if(s[i]==s[j+1])++j; 11 nex[i]=j; 12 } 13 } 14 struct node{ 15 int n,m,s[25][25]; 16 node(){ 17 n=m=0;memset(s,0,sizeof(s)); 18 } 19 }; 20 node mul(node a,node b) 21 { 22 node c;c.n=a.n;c.m=b.m; 23 for(int k=0;k<a.m;++k) 24 for(int i=0;i<a.n;++i) 25 for(int j=0;j<b.m;++j) 26 { 27 c.s[i][j]=(c.s[i][j]+a.s[i][k]*b.s[k][j]%mod)%mod; 28 } 29 return c; 30 } 31 node qmod(node a,int x) 32 { 33 node c;c.n=a.n;c.m=a.m; 34 for(int i=0;i<a.n;++i)c.s[i][i]=1; 35 while(x) 36 { 37 if(x&1)c=mul(c,a); 38 x>>=1;a=mul(a,a); 39 } 40 return c; 41 } 42 int main() 43 { 44 char s[25];int k; 45 scanf("%d%d%d",&n,&m,&k);mod=k; 46 scanf("%s",s+1);setnex(s,m); 47 node p,ans;p.n=p.m=m; 48 for(int i=0;i<m;++i) 49 { 50 for(char j='0';j<='9';++j) 51 { 52 int k=i; 53 while(k&&s[k+1]!=j)k=nex[k]; 54 if(s[k+1]==j)p.s[i][k+1]++; 55 else p.s[i][0]++; 56 } 57 } 58 ans.n=1,ans.m=m; 59 ans.s[0][0]=1; 60 ans=mul(ans,qmod(p,n)); 61 int sum=0; 62 for(int i=0;i<m;++i)sum=(sum+ans.s[0][i])%mod; 63 printf("%d",sum); 64 return 0; 65 }
生命中真正重要的不是你遭遇了什么,而是你记住了哪些事,又是如何铭记的。