1. 程式人生 > >P3193 [HNOI2008]GT考試

P3193 [HNOI2008]GT考試

傳送門

容易看出是道DP

考慮一位一位填數字

設 f [ i ] [ j ] 表示填到第 i 位,在不吉利串上匹配到第 j 位時不出現不吉利數字的方案數

設 g [ i ] [ j ] 表示不吉利串匹配到第 i 位,再新增一個數字,使串匹配到第 j 位的方案數

那麼方程顯然為 : 

 

注意我們不需要考慮 $j=m$ 的情況,因為 $j=m$時肯定已經出現匹配了

顯然我們可以預處理出 g ,然後直接轉移

最後答案就是 

 

還有一個問題,n 太大了

發現 g 是固定的,把 g 搞成矩陣直接矩陣加速一下

複雜度$ O(log_n)$

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
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; } const int N=27; int n,m,mo; inline int fk(int x) { return x>=mo ? x-mo : x; } int a[N],fail[N]; char s[N]; int g[N][N]; struct matrix//矩陣不解釋 { int a[N][N]; matrix () { memset(a,
0,sizeof(a)); } inline matrix operator * (const matrix &tmp) const { matrix res; for(int i=0;i<m;i++) for(int j=0;j<m;j++) for(int k=0;k<m;k++) res.a[i][j]=fk(res.a[i][j]+a[i][k]*tmp.a[k][j]%mo); return res; } }F,M; inline matrix ksm(matrix x,int y)//矩陣快速冪不解釋 { matrix res; for(int i=0;i<=m;i++) res.a[i][i]=1; while(y) { if(y&1) res=res*x; x=x*x; y>>=1; } return res; } inline void pre()//預處理,本人閒的蛋疼用kmp預處理g { int x=0; fail[0]=-1; for(int i=1;i<=m;i++) { x=fail[i-1]; while(x!=-1&&a[x+1]!=a[i]) x=fail[x]; fail[i]=x+1; } fail[0]=0; for(int i=0;i<m;i++) for(int j=0;j<10;j++)//列舉填的每個數字,看看能匹配到哪裡 { x=i; while(x&&a[x+1]!=j) x=fail[x]; g[i][a[x+1]==j ? x+1 : x]++;//把匹配到的位置++ } for(int i=0;i<m;i++) for(int j=0;j<m;j++) M.a[i][j]=g[i][j];//轉移矩陣就是g } int main() { n=read(); m=read(); mo=read(); scanf("%s",s+1); for(int i=1;i<=m;i++) a[i]=s[i]-'0'; a[m+1]=a[0]=-1;//閒的蛋疼,就是愛轉數字 pre(); F.a[0][0]=1;//初始狀態 F=F*ksm(M,n); int ans=0; for(int i=0;i<m;i++) ans=fk(ans+F.a[0][i]); printf("%d",ans); return 0; }