[BZOJ1009][HNOI2008]GT考试(KMP+DP+矩阵乘法)
1009: [HNOI2008]GT考试
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 4626 Solved: 2878
[Submit][Status][Discuss]Description
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为
0Input
第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000
Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
Sample Input
4 3 100
111Sample Output
81HINT
Source
[Submit][Status][Discuss]
这题要是多模式串可能还容易看出来一些,discuss里有单串AC自动机做法,但是“单串AC自动机”不就是KMP吗。
首先看数据范围猜是矩乘,然后有了显然的DP方程,就可以根据转移递推式上矩乘优化了。
https://blog.csdn.net/jeremygjy/article/details/50779475
递推式里的p(k,j)需要用KMP处理,仅当k<=j+1时值非零。
注意矩阵只要从0~m-1,因为匹配m位的情况要全部去除。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=30; 9 char s[N]; 10 int n,m,p,k,ans,nxt[N]; 11 struct M{ int a[N][N]; M(){ memset(a,0,sizeof(a)); } }f,res; 12 13 void getnxt(){ 14 nxt[0]=nxt[1]=0; 15 rep(i,2,m){ 16 for (k=nxt[i-1]; k && s[k+1]!=s[i]; k=nxt[k]); 17 if (s[k+1]==s[i]) nxt[i]=k+1; else nxt[i]=0; 18 } 19 } 20 21 void mul(M &a,M b){ 22 M c; 23 rep(i,0,m-1) rep(j,0,m-1) rep(k,0,m-1) c.a[i][k]=(c.a[i][k]+a.a[i][j]*b.a[j][k])%p; 24 rep(i,0,m-1) rep(j,0,m-1) a.a[i][j]=c.a[i][j]; 25 } 26 27 M ksm(M a,int b){ 28 M c; 29 rep(i,0,m-1) c.a[i][i]=1; 30 for (; b; mul(a,a),b>>=1) 31 if (b & 1) mul(c,a); 32 return c; 33 } 34 35 int main(){ 36 freopen("bzoj1009.in","r",stdin); 37 freopen("bzoj1009.out","w",stdout); 38 scanf("%d%d%d",&n,&m,&p); scanf("%s",s+1); 39 getnxt(); 40 rep(i,0,m-1){ 41 for (char j='0'; j<='9'; j++){ 42 for (k=i; k && s[k+1]!=j; k=nxt[k]); 43 if (s[k+1]==j) f.a[i][k+1]=(f.a[i][k+1]+1)%p; else f.a[i][0]=(f.a[i][0]+1)%p; 44 } 45 } 46 res.a[0][0]=1; mul(res,ksm(f,n)); 47 rep(i,0,m-1) ans=(ans+res.a[0][i])%p; 48 printf("%d\n",ans); 49 return 0; 50 }