【BZOJ1009/HNOI2008】GT考试

                                     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位的方案数。

扫描二维码关注公众号,回复: 2755782 查看本文章

       于是答案就为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;
}

猜你喜欢

转载自blog.csdn.net/m0_38083668/article/details/81557765