BZOJ-1009 [HNOI2008]GT考试(矩阵快速幂加速dp+KMP)
题目描述
准考证号为 \(n\) 位数 \(X_1,X_2,\cdots,X_n(0\leq X_i\leq 9)\),不希望准考证号上出现不吉利的数字。不吉利数字 \(A_1,A_2,\cdots,A_m(0\leq A_i\leq 9)\) 有 \(m\) 位,不出现是指 \(X_1,X_2,\cdots,X_n\) 中没有恰好一段等于 \(A_1,A_2,\cdots,A_m\),\(A_1\) 和 \(X_1\)可以为 \(0\)。求合法方案数,答案对 \(p\) 取模。
数据范围:\(n\leq 10^9,m\leq 20,p\leq 1000\)。
分析
设 \(dp[i][j]\) 表示准考证的前 \(i\) 位数中的最后 \(j\) 位与不吉利的数字的前 \(j\) 位相同时,前 \(i\) 位的合法方案数,且 \(dp[i][j]\) 的每种方案都不含长度大于 \(j\) 且与不吉利数字的前缀相同的后缀(为了避免重复)。则答案为:\(dp[n][0]+dp[n][1]+\cdots+dp[n][m-1]\)。
考虑如何进行状态转移,\(dp[i][j]\) 只能由 \(dp[i-1][k]\) 转移过来,相当于填完第 \(i-1\) 位后,长为 \(k\) 的后缀后面新添加一位 \(num\),此时这个有 \(i\) 位的数字与不吉利数字前缀相同的最长的后缀的长度为 \(j\)。
状态转移方程为:
上式的 \(f[k][j]\) 表示当前准考证的长为 \(k\) 的后缀已经匹配了不吉利数字长为 \(k\) 的前缀,有多少种添加一个数字 \(num\) 的方法,能使匹配长度变为 \(j\)。
举个例子:假设不吉利数字是 \(123124\),则 \(dp[i][3]=dp[i-1][2]+dp[i-1][5]\),因为 \(dp[i-1][2]\) 的后缀\(\cdots12\) 不能是 \(\cdots12312\),所以还需要 \(dp[i-1][5]\) 来补充;假设不吉利数字是 \(123123\),则 \(dp[i][3]=dp[i-1][2]\),因为 \(dp[i][3]\) 末尾的 \(\cdots123\) 不能是 \(\cdots123123\)。
因为我们现在已经知道不吉利数字是什么,所以 \(f[k][j]\) 矩阵是固定的,可以用 \(\text{KMP}\) 算法预处理 \(\text{Next}\) 数组,然后枚举长度 \(k\) 和添加的数字 $num $,沿着 \(\text{Next}\) 数组往前跳找到来找到能转移到的 \(j\),从而预处理出 \(f[k][j]\) 数组。
可以发现这个状态转移方程和矩阵乘法的的式子非常像,用矩阵快速幂加速 $dp $ 即可,时间复杂度 \(O(m^3\log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,mod,Next[30];
char s[30];
struct matrix
{
int mat[30][30];
matrix()
{
memset(mat, 0, sizeof(mat));
}
}A;
matrix mul(matrix A,matrix B)
{
matrix ans;
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
ans.mat[i][j]=(ans.mat[i][j]+A.mat[i][k]*B.mat[k][j])%mod;
return ans;
}
matrix matrix_pow(matrix a,int b)
{
matrix ans;
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
ans.mat[i][j]=(i==j);
while(b)
{
if(b&1)
ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
void get_next()
{
Next[1]=0;
int j=0;
for(int i=2;i<=m;i++)
{
while(j>0&&s[i]!=s[j+1])
j=Next[j];
if(s[i]==s[j+1])
j++;
Next[i]=j;
}
}
matrix KMP()
{
get_next();
matrix ans;
for(int i=0;i<=m-1;i++)
{
for(char num=0;num<=9;num++)
{
int j=i;
while(j>0&&num!=(int)(s[j+1]-'0'))
j=Next[j];
if(num==(int)(s[j+1]-'0'))
j++;
ans.mat[i][j]++;
}
}
return ans;
}
matrix f;
int main()
{
cin>>n>>m>>mod;
scanf("%s",s+1);
f=matrix_pow(KMP(),n);
int ans=0;
for(int i=0;i<=m-1;i++)
ans=(ans+f.mat[0][i])%mod;
cout<<ans<<endl;
return 0;
}
posted on 2020-11-24 18:57 DestinHistoire 阅读(70) 评论(0) 收藏 举报
浙公网安备 33010602011771号