「HNOI2008」GT考试

继续学数数喽QwQ…

传送门

洛谷P3193

BZOJ1009

题解

很显然这是一道优化DP数数题 ,可惜我又不会QwQ。

此处省略0x3F3F3F3F字的思考过程

令$f[i][j]​$表示长串匹配到第$i​$位,短串匹配到第$j​$位的合法方案数;

$g[k][j]$表示短串匹配到第$j$位时,长串再加上一个字符使得最多能匹配$k$位的方案数。

那么就有

最后的答案就是

观察一下DP转移方程,发现$g$数组是固定的,可以通过看毛片KMP或者暴力预处理出来(抱歉我这里真的没有毛片看,但是据说隔壁巨佬知道哪里有)。

再狠狠瞅几眼转移方程,艾玛!这不是一个矩阵乘法的式子吗!

令矩阵$F[i]$的第一行为$f[i][j]​$,那么就有

所以

且又知道$f[0][0]=1$、最后答案为$F[n]​$第一行元素之和。

所以这题就解完了。

代码

本蒟蒻因为矩阵重载乘法运算符是忘了return调了半天QwQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,K,nxt[25],ans;char S[25];
struct Matrix //矩阵类
{
int A[25][25];
Matrix(){memset(A,0,sizeof(A));}
Matrix operator * (const Matrix& b) //矩阵乘法
{
Matrix c;
for(int i=0;i<m;i++)
{
for(int j=0;j<m;j++)
{
for(int k=0;k<m;k++)
{
c.A[i][j]+=A[i][k]*b.A[k][j];
c.A[i][j]%=K; //记得mod
}
}
}
return c;
}
}G,F;
inline void KMP() //KMP构造矩阵G
{
for(int i=2;i<=m;i++)
{
int j=nxt[i-1];
while(j&&S[i]!=S[j+1]) j=nxt[j];
if(S[i]==S[j+1]) nxt[i]=j+1;
}
for(int i=0;i<m;i++)
{
for(int j='0';j<='9';j++)
{
int k=i;
while(k&&S[k+1]!=j) k=nxt[k];
if(S[k+1]==j) k++;
if(k<m) G.A[i][k]++;
}
}
}
Matrix QP(const Matrix& a,int b) //矩阵快速幂
{
Matrix ret=a,w=a;b--;
while(b)
{
if(b&1) ret=ret*w;
w=w*w;b>>=1;
}
return ret;
}
int main()
{
scanf("%d%d%d%s",&n,&m,&K,S+1);
KMP();F.A[0][0]=1;F=F*QP(G,n);
for(int i=0;i<m;i++) ans=(ans+F.A[0][i])%K;
printf("%d\n",ans);
return 0;
}