BZOJ KMP+矩阵快速幂 1009: [HNOI2008]GT考试

1009: [HNOI2008]GT考试

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 1416   Solved: 851
[ Submit][ Status]

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位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6

Output

阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

Sample Input

4 3 100
111

Sample Output

81

HINT

Source

[ Submit][ Status]


 

 

题意:有一个有n位的学号,每个位是0~9的任意一个数,给出一个长度为m的串,问n位的学号有多少种情况是子串中不含那个长度为m的串。

 

思路:我们的状态可以表示为当前匹配到那个m串的什么位置,然后转移的时候是根据下一个是0~9的哪一个就对应的转移到了匹配m串的什么位置。 所以这里我们能用KMP算出m串的失败指针,然后再算出假设插入0~9中的其中一个,看某个位置能转移到哪个位置,假设是从i转移到了j,那么我们在矩阵的(i,j)处+1,表示 i可以转移到 j。由于n非常大,所以直接递推是不实际的。 我们把状态做成一个1*(M+1)的矩阵,设为A,对应的(0,i) 的数字表示当前匹配了m串的0~i-1 的有多少个串。 然后转移的关系也是一个矩阵 大小是(M+1)*(M+1), 设为B,AxB其实表示的就是n串的长度为1的时候的状态,如果在乘一次B,那么就是长度为2的时候的状态,所以长度为n的时候就是Ax(B)^n , 那么这里我们就能用矩阵快速幂来处理了。 最后的话得到的是一个1*(m+1)的矩阵,我们只需要把0~(m-1)列的数加起来,就是题目要求的数了

 

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;
#define LL long long
char s[25];
int f[25];
LL n, m, mod;

int A[25][25];

void getFail()
{
	f[0] = 0; f[1] = 0;
	for (int i = 1; i < m; ++i) {
		int j = f[i];
		while (j&&s[i] != s[j]) j = f[j];
		f[i + 1] = s[i] == s[j] ? j + 1 : 0;
	}
}

void getMatrix()
{
	memset(A, 0, sizeof(A));
	for (int i = 0; i < m; ++i) {
		for (char j = '0'; j <= '9'; ++j) {
			int k = i;
			while (k&&s[k] != j) k = f[k];
			if (s[k] == j) ++k;
			++A[i][k];
		}
	}
	A[m][m] = 10;
}

void Multiple(int M1[25][25], int n1, int m1, int M2[25][25], int n2, int m2)
{
	int ret[25][25];
	memset(ret, 0,  sizeof(ret));
	for (int i = 0; i < n1;++i)
	for (int j = 0; j < m2; ++j)
	for (int k = 0; k < m1; ++k)
		ret[i][j] = (ret[i][j]+M1[i][k] * M2[k][j])%mod;
	memcpy(M1, ret, sizeof(ret));
}

void qpow(int M[25][25],int n, int p)
{
	int ret[25][25];
	memset(ret, 0, sizeof(ret));
	for (int i = 0; i < n; ++i) ret[i][i] = 1;
	while (p>0) {
		if (p & 1) Multiple(ret, n, n, M, n, n);
		Multiple(M, n, n, M, n, n);
		p >>= 1;
	}
	memcpy(M, ret, sizeof(ret));

}

void solve()
{
	getFail();
	getMatrix();
	qpow(A, m + 1, n);
	int B[25][25];
	memset(B, 0, sizeof(B));
	B[0][0] = 1;
	Multiple(B, 1, m + 1, A, m + 1, m + 1);
	int sum = 0;
	for (int i = 0; i < m; ++i)
		sum = (sum + B[0][i]) % mod;
	printf("%d\n", sum);
}

int main()
{
	while (scanf("%lld%lld%lld", &n, &m, &mod) == 3)
	{
		scanf("%s",s);
		solve();
	}
}


 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值