1. 程式人生 > 實用技巧 >[HNOI2008]GT考試

[HNOI2008]GT考試

[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]\)
    對於一個匹配到長度為 \(j\)的串,轉移到 \(k\) 的串的方案
  • 此時轉移方程\(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;
}