bzoj1009: [HNOI2008]GT考试
题解:
首先看到这道题n特别大,m很小,又有着很明显的状态转移,基本就可以看出这是一道矩阵优化dp题目了(终于不是在看完题解后再说了QAQ)
可以比较容易的看出状态转移,设f[i][j]表示长度为n的串匹配到了i,长度为m的串匹配到了j,预先处理m串的kmp,找到m串如何转移(我第一次写没有注意到需要处理kmp这回事,直接不符合的话转移到f[i][0]上,这样实际上是漏掉了情况);
然后构造一个使f[i]转移到f[i+1]的矩阵B,使f[i]*B=f[i+1];
然后用矩阵快速幂就行了;
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<ctime> 7 #include<vector> 8 #include<algorithm> 9 #include<queue> 10 #include<map> 11 #include<vector> 12 #include<cmath> 13 using namespace std; 14 #define LL long long 15 #define FILE "1" 16 #define up(i,j,n) for(int i=j;i<=n;i++) 17 #define down(i,n,j) for(int i=n;i>=j;i--) 18 int n,m,k; 19 const int maxn=30; 20 char s[maxn]; 21 struct M{ 22 int v[maxn][maxn]; 23 M(){memset(v,0,sizeof(v));} 24 friend M operator*(M a,M b){ 25 M c; 26 for(int i=0;i<m;i++) 27 for(int j=0;j<m;j++) 28 for(int h=0;h<m;h++) 29 c.v[i][j]=(c.v[i][j]+a.v[i][h]*b.v[h][j])%k; 30 return c; 31 } 32 M operator^(int b){ 33 M c; 34 for(int i=0;i<m;i++)c.v[i][i]=1; 35 while(b){ 36 if(b%2)c=c*(*this); 37 b/=2; 38 *this=*this*(*this); 39 } 40 return c; 41 } 42 }a,b; 43 int next[maxn]; 44 int main(){ 45 //freopen("1.in","r",stdin); 46 scanf("%d%d%d",&n,&m,&k); 47 scanf("%s",s+1); 48 int j=0;next[1]=0; 49 for(int i=2;i<=m;i++){ 50 while(j&&s[j+1]!=s[i])j=next[j]; 51 if(s[j+1]==s[i])j++; 52 next[i]=j; 53 } 54 up(i,0,(m-1))up(j,0,9){ 55 int t=i; 56 while(t&&s[t+1]-'0'!=j)t=next[t]; 57 if(s[t+1]-'0'==j)t++; 58 b.v[i][t]=(b.v[i][t]+1)%k; 59 } 60 a.v[0][0]=1; 61 b=b^n; 62 a=a*b; 63 int sum=0; 64 for(int i=0;i<m;i++)sum=(sum+a.v[0][i])%k; 65 printf("%d\n",sum); 66 return 0; 67 }
//这样的矩阵乘法时间复杂度很高,如果矩阵乘法的内容比较少还是自己写个比较好