[HNOI2008]GT考試
阿新 • • 發佈:2020-07-22
[HNOI2008]GT考試
題目描述
阿申準備報名參加 GT 考試,准考證號為 \(N\) 位數\(X_{1},X_{2}…X_{n}(0≤X_{i}≤9)\),他不希望准考證號上出現不吉利的數字。 他的不吉利數學\(A_{1},A_{2}…A_{m}(0≤A_{i}≤9)\) 有 \(M\) 位,不出現是指 \(X_{1},X_{2}…X_{n}\) 中沒有恰好一段等於 \(A_{1},A_{2}…A_{m}\),\(A_{1}\) 和\(X_{1}\) 可以為 \(0\)
輸入格式
第一行輸入\(N\),\(M\),\(K\).接下來一行輸入M位的數。
輸出格式
阿申想知道不出現不吉利數字的號碼有多少種,輸出模 \(K\)
輸入輸出樣例
輸入 #1
4 3 100
111
輸出 #1
81
說明/提示
\(N≤10^{9},M≤20,K≤1000\)
思路分析
本來就是想好好想練一下\(KMP\),根據某谷標籤找到了這道題,看題面那麼簡短,以為並不是很難(雖然是紫的),結果做著做著就傻了
這題很綜合,\(DP\)+矩陣快速冪+\(KMP\),我原地螺旋昇天sto orz
狀態轉移:
- 設\(dp[i][j]\)表示長度為\(i\)的字串,最後\(j\)個可以匹配模式串前\(j\)位的情況數,答案就是\(\sum_{i=0}^{m-1}dp[n][i]\)
- However,理論是有了,如何實現?
- 關鍵在於對模式串的不同匹配情況,用一個\(g[k][j]\)
- 此時轉移方程\(dp[i][j] = \sum_{k=0}^{m-1}dp[i-1][k]*g[k][j]\),你看,你看,你仔細看,這個式子像什麼?矩陣乘啊!一個矩陣快速冪就能優化了!(本蒟蒻永遠推不出來系列)
等會兒,我不是來練\(KMP\)的嗎???
Code
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 21 using namespace std; inline int read(){ int x = 0,f = 1; char ch = getchar(); while(ch<'0'||ch>'9'){if(ch='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } int n,m,a[N],kmp[N],f[N][N],mod; char s[N]; struct xzy{ //定義矩陣 int n,m,a[N][N]; xzy(){n=m=0;memset(a,0,sizeof(a));} void clear(){n=m=0;memset(a,0,sizeof(a));} }A,B; void mul(xzy &a,xzy b){//矩陣乘法 xzy ret; ret.n = a.n;ret.m = b.m; for(int i = 0;i <= ret.n;i++){ for(int k = 0;k <= a.m;k++){ if(!a.a[i][k])continue; for(int j =0;j<=ret.m;j++){ ret.a[i][j] =(ret.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod; } } } a = ret; } void quickpow(xzy &x,xzy &y,int t){//矩陣快速冪 while(t){ if(t&1)mul(x,y); mul(y,y); t>>=1; } } int main(){ m = read(),n =read(),mod = read(); scanf("%s",s); for(int i = 0;i < n;i++)a[i]=s[i]-'0'; a[n] = 0x3f3f3f3f; for(int i = 1,j=0;i < n;i++){ //這好像才是我的本意……,板子就不解釋了 while(j&&a[i]!=a[j])j = kmp[j]; j+=(a[i]==a[j]); kmp[i+1] = j; } for(int i=0;i<n;i++){ for(int j=0;j<10;j++){//等同於上面的g陣列 int k=i; while(k&&a[k]!=j)k=kmp[k]; k+=(a[k]==j); if(k<n) B.a[i][k]+=1; //只要沒匹配成功,該情況成立 } } B.m=B.n=A.m=n-1;A.n=0;A.a[0][0]=1;//初始化矩陣,A相當與上面的dp陣列 quickpow(A,B,m); int ans=0; for(int i=0;i<n;i++) ans+=A.a[0][i],ans%=mod; printf("%d",ans); return 0; }