bzoj1009-HNOI2008 GT考試 字符串dp+矩陣快速冪
阿新 • • 發佈:2019-04-20
表示 我們 ios inline gt考試 urn zoj 時間 ostream
題面描述
- 阿申準備報名參加\(GT\)考試,準考證號為\(N\)位數\(x_1,x_2,...,x_n\ (0\leq x_i\leq 9)\),他不希望準考證號上出現不吉利的數字。
他的不吉利數字\(a_1,a_2,...,a_m\ (0\leq a_i\leq 9)\)有\(M\)位,不出現是指\(x_1,x_2,...,x_n\)中沒有恰好一段等於\(a_1,a_2,...,a_m\)。 \(a_1\)和\(x_1\)可以為\(0\)
- 阿申準備報名參加\(GT\)考試,準考證號為\(N\)位數\(x_1,x_2,...,x_n\ (0\leq x_i\leq 9)\),他不希望準考證號上出現不吉利的數字。
輸入格式
- 第一行輸入\(N,M,K\)。接下來一行輸入\(M\)位的數。 \(N\leq 10^9,M\leq 20,K\leq 1000\)
輸出格式
- 阿申想知道不出現不吉利數字的號碼有多少種,輸出模\(K\)
- 阿申想知道不出現不吉利數字的號碼有多少種,輸出模\(K\)
題解
首先,看到題意是在一定條件下統計 位數\(\leq N\)的數 的個數,第一反應數位\(dp\)。題目對要統計的數的要求是 這個數不能與模式串(不吉利數字)匹配。我們回憶\(KMP\)過程,當原串與模式串在某一位失配時,我們將模式串指針\(x\)通過\(next_x\)不斷回跳,直到能夠與原串匹配。
類似的,當我們按照數位\(dp\)的階段,在後面加上\(0-9\)中的數字\(x\)時,我們同樣通過\(next_x\)匹配,再在尾部加上數字\(x\)。
因此我們可以設計出這樣的\(dp\)方程。令\(f_{i,j}\)表示前\(i\)位匹配到模式串的第\(j\)位的方案數,令\(pre_{i,0..9}\)
\[ f_{i,pre_{j,x}}+=f_{i-1,j}\ (0\leq j<m,0\leq x\leq 9) \]
這樣我們得到了一個時間復雜度為\(O(nm)\)的
優秀算法。再看一眼範圍\(n\leq 10^9\)!!這樣我們就只能用加速線性遞推式的神器矩陣快速冪。將遞推式寫成矩陣的形式,用矩陣快速冪.....
(感覺根本不會講)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=25; int n,m,mod; int a[MAXN]; int nxt[MAXN]; struct rec{ int a[MAXN][MAXN]; rec(){ for (int i=0;i<=m;i++){ for (int j=0;j<=m;j++) a[i][j]=0; } } } A; rec mul(rec a,rec b){ rec c; for (int k=0;k<=m;k++){ for (int i=0;i<=m;i++){ for (int j=0;j<=m;j++){ c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod; } } } return c; } rec mod_pow(rec a,int n){ rec ans=a; n--; while (n){ if (n&1) ans=mul(ans,a); a=mul(a,a); n>>=1; } return ans; } int main(){ scanf("%d%d%d",&n,&m,&mod); for (int i=1;i<=m;i++){ char c=getchar(); while (c<'0'||c>'9') c=getchar(); a[i]=c-'0'; } // cout<<"done"<<endl; nxt[1]=0; for (int i=2;i<=m;i++){ int pre=nxt[i-1]; while (pre>0&&a[pre+1]!=a[i]) pre=nxt[pre]; if (a[pre+1]==a[i]) pre++; nxt[i]=pre; } // cout<<"done"<<endl; for (int i=0;i<m;i++){ for (int j=0;j<=9;j++){ // cout<<i<<" "<<j<<endl; int pre=i; while (pre>0&&a[pre+1]!=j) pre=nxt[pre]; if (a[pre+1]==j) pre++; if (pre!=m) A.a[pre][i]=(A.a[pre][i]+1)%mod; } } // cout<<"done"<<endl; A=mod_pow(A,n); int ans=0; for (int i=0;i<m;i++) ans=(ans+A.a[i][0])%mod; printf("%d\n",ans); return 0; }
bzoj1009-HNOI2008 GT考試 字符串dp+矩陣快速冪