BZOJ1009: [HNOI2008]GT考试 矩阵快速幂+kmp+dp
这个题你发现打暴力的话可以记忆化搜素加剪枝,那么意味着可以递推,我们搜的话就是1010^9我们就往下匹配遇到匹配成功就return,那么我们可以想一下什么决定了状态,我们考虑kmp的过程,对于我们目前匹配到的距离,下一次在匹配时不会用他之后的字符,那么只要我们知道匹配到的距离和已匹配长度就行了,那么我们考虑状态的转移,我们由于要像kmp那样匹配于是我们只要知道在匹配到k位时往下走一个数时匹配到哪,算出a[k][j](在k时到j的方案数),那么新的f[i][j]=∑f[i-1][k]*a[k][j],这里用到了对口遗传的思想,对于舍去的状态,我们不继承就是舍去了
至于钜乘(:••)去某位大佬博客
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int n,m,k; char s[50]; int next[50]; int a[50][50],b[50][50],temp[50][50]; inline void kmp() { a[m-1][m-0]+=1; a[m-0][m-0]+=9; for(int i=1,K=0;i<m;i++) { while(K&&s[K]!=s[i]) K=next[K-1]; if(s[K]==s[i]) K++; next[i]=K; for(int j=0;j<=9;j++) { if(j+48==s[i]) { a[m-i-1][m-i]+=1; continue; } int l=next[i-1]; while(l&&s[l]!=j+48) l=next[l-1]; if(s[l]==j+48) l++; a[m-l][m-i]+=1; } } } inline void Init() { scanf("%d%d%d%s",&n,&m,&k,s); kmp(); } inline void multi(int x[][50],int y[][50],int len) { memset(temp,0,sizeof(temp)); for(int i=1;i<=m;i++) for(int j=1;j<=len;j++) for(int l=1;l<=m;l++) temp[i][j]=(temp[i][j]+y[i][l]*x[l][j])%k; for(int i=1;i<=m;i++) for(int j=1;j<=len;j++) x[i][j]=temp[i][j]; } inline void work() { b[m][1]=1; while(n) { if(n&1)multi(b,a,1); n>>=1; multi(a,a,m); } } inline void print() { int ans=0; for(int i=1;i<=m;i++) ans+=b[i][1]; ans%=k; printf("%d",ans); } int main() { Init(); work(); print(); return 0; }
苟利国家生死以, 岂因祸福避趋之。