1009: [HNOI2008]GT考试
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 4869 Solved: 3052
Description
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为
0
Input
第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000
Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
Sample Input
4 3 100
111
Sample Output
81
前言:
感觉网上的题解都不是很清晰,绞尽脑汁后我尽量让大家看懂。
解析:
DP + 矩阵加速 + KMP。
令 f[ i ][ j ]代表准考证号前 i 位中后 j 位与不吉利数的前 j 位相同时,前i位的方案数。
于是答案就为ans=ΣW[N,I] 0<=I<=M-1。
考虑怎么进行转移。假设当前到第 i 位了,匹配到第 i 位,我们可以枚举第I+1位是什么,然后通过KMP的NEXT数组可以快速的得到当前枚举的位可以匹配到第几位,假设可以匹配到第P位,那么我们 f[ I + 1 , P ] += f[ I , J ],这样就可以转移了。于是最终状态转移方程为:
f[ i ][ j ] = f[ i - 1][ 0 ] * a[ 0 ][ j ] + f[ i - 1 ][ 1 ] * a[ 1 ][ j ] + … + f[ i - 1 ][ m - 1 ] * a[m - 1][ j ]
其中a[ i ][ j ]表示从第 i 位转移到第 j 位可能的方案数。
但有一个问题,直接进行递推肯定是不行的。我们发现这个递推式与矩阵乘法的公式很相似,于是可以转化为矩阵乘法,用矩阵快速幂在 log 级别的时间内求得答案了。并且我们发现 f[ i ][] 都是从f[ i-1 ][] 递推过来的,所以我们可以直接省略掉第一维。我的代码中为了方便没有省去。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=25;
int n,m,mod,sum;
int ans[Max][Max],a[Max][Max],b[Max][Max],next[Max];
char ch[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void kmp()
{
for(int i=2,j=0;i<=m;i++)
{
while(j&&ch[i]!=ch[j+1]) j=next[j];
if(ch[i] == ch[j+1]) j++;
next[i]=j;
}
}
inline void pre()
{
for(int i=0;i<m;i++)
for(int j=0;j<=9;j++)
{
int t=i;
while(t && ch[t+1]-'0'!=j) t=next[t];
if(ch[t+1]-'0'==j) t++;
a[i][t]++;
}
}
inline void mul()
{
memset(b,0,sizeof(b));
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
b[0][j] = (b[0][j] + (long long)ans[0][k] * a[k][j]) % mod;
memcpy(ans,b,sizeof(b));
}
inline void mulself()
{
memset(b,0,sizeof(b));
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
b[i][j] = (b[i][j] + (long long)a[i][k] * a[k][j]) % mod;
memcpy(a,b,sizeof(b));
}
inline void solve()
{
ans[0][0]=1;
while(n)
{
if(n&1) mul();
n>>=1;
mulself();
}
for(int i=0;i<m;i++) sum = (sum + ans[0][i]) % mod;
cout<<sum<<"\n";
}
int main()
{
n=get_int(),m=get_int(),mod=get_int();
scanf("%s",ch+1);
kmp();
pre();
solve();
return 0;
}