[HNOI2008] GT考試 - KMP,矩陣乘法,dp
阿新 • • 發佈:2020-07-06
Description
准考證號為 \(N\) 位數 \(X_1,X_2…X_n(0\le X_i\le9)\),他不希望准考證號上出現不吉利的數字。他的不吉利數 \(A_1,A_2…A_m(0\le A_i\le 9)\) 有 \(M\) 位,不出現是指 \(X_1,X_2…X_n\) 中沒有恰好一段等於 \(A_1,A_2…A_m\),\(A_1\) 和 \(X_1\) 可以為 \(0\)。
Solution
設 \(next[i]\) 表示 \(A[1..i]\) 的最長公共真前後綴,可以 KMP 預處理出
構造矩陣,當匹配到 \(i\) 位置 \(+c\) 可以轉移到 \(p\) 位置時,對 \(mat[p][i]++\)
這就是 DP 的轉移矩陣,快速冪處理即可
(實際上最後我們只需要統計冪矩陣的第一列的和)
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 25; int n,m,mod; char s[N]; namespace kmp { char *p=s; int n,m,fail[N]; void main() { m=strlen(p+1); for(int i=2; i<=m; i++) { fail[i]=fail[i-1]; while(p[fail[i]+1]-p[i] && fail[i]) fail[i]=fail[fail[i]]; if(p[fail[i]+1]==p[i]) ++fail[i]; } fail[0]=-1; } } struct matrix { int n,a[N][N]; matrix(int n=0):n(n) { memset(a,0,sizeof a); } int* operator [] (int i) { return a[i]; } friend matrix operator * (matrix a,matrix b) { int n=a.n; matrix res(n); for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { for(int k=1; k<=n; k++) { res[i][k]=((res[i][k]+a[i][j]*b[j][k])%mod+mod)%mod; } } } return res; } matrix I() { matrix res(n); for(int i=1; i<=n; i++) { res[i][i]=1; } return res; } matrix operator ^ (int b) { if(b) return (((*this)*(*this))^(b/2))*(b&1?(*this):I()); return I(); } void print() { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) cout<<a[i][j]<<"\t"; cout<<endl; } } }; signed main() { ios::sync_with_stdio(false); cin>>n>>m>>mod>>s+1; kmp::main(); matrix mat(m); using kmp::fail; for(int i=0;i<m;i++) { for(char c='0';c<='9';c++) { int p=i; while(p && s[p+1]!=c) p=fail[p]; if(s[p+1]==c) ++p; ++mat[p+1][i+1]; } } //mat.print(); mat=mat^n; //mat.print(); int ans=0; for(int i=1;i<=m;i++) ans=(ans+mat[i][1])%mod; cout<<ans<<endl; }