题目描述
阿申准备报名参加 GT 考试,准考证号为 N 位数X1,X2…Xn(0≤Xi≤9)X_1,X_2…X_n(0\le X_i\le9)X1,X2…Xn(0≤Xi≤9),他不希望准考证号上出现不吉利的数字。 他的不吉利数学A1,A2…Am(0≤Ai≤9)A_1,A_2…A_m(0\le A_i\le 9)A1,A2…Am(0≤Ai≤9) 有 M 位,不出现是指 X1,X2…XnX_1,X_2…X_nX1,X2…Xn中没有恰好一段等于 A1,A2…AmA_1,A_2…A_mA1,A2…Am,A1A_1A1 和X1X_1X1 可以为 0
输入格式
第一行输入N,M,K.接下来一行输入M位的数。
输出格式
阿申想知道不出现不吉利数字的号码有多少种,输出模 K 取余的结果。
输入输出样例
输入 #1
4 3 100
111
输出 #1
81
说明/提示
N≤109,M≤20,K≤1000N≤10^9,M≤20,K≤1000N≤109,M≤20,K≤1000
解释:是DPDPDP,我们设dp[i][j]:[1,i]dp[i][j]:[1,i]dp[i][j]:[1,i]中与不吉利串匹配前jjj的个数,那么
dp[i][j]=∑k=1m−1dp[i−1][k]∗gk,jdp[i][j]=\sum_{k=1}^{m-1}dp[i-1][k]*g_{k,j}dp[i][j]=∑k=1m−1dp[i−1][k]∗gk,j,其中gk,jg_{k,j}gk,j是匹配不吉利串前j个加入一个字符后变成前kkk个的个数,gk,jg_{k,j}gk,j的化我们可以通过KMP的next数组计算出来,然后观察dpdpdp方程可以转化成矩阵乘,所以我们可以用矩阵快速幂加速
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
#define N 30
int mod=0;
class Matrix{
public:
ll a[N][N],n;
Matrix(ll _n){
n=_n;
memset(a,0,sizeof(a));
for(int i=0;i<n;i++) a[i][i]=1%mod;
}
ll get(int x,int y){
return a[x][y];
}
void set(int x,int y,int val){
a[x][y]=val%mod;
}
void mul(Matrix &b){
ll c[N][N]={0};
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
c[i][j]+=a[i][k]*b.a[k][j]%mod;
c[i][j]%=mod;
}
}
}
for(int i=0;i<n;i++)
for(int j=0;j<n;j++) a[i][j]=c[i][j];
}
void pow(ll k){
Matrix ret(n);
while(k){
if(k&1){
ret.mul(*this);
}k>>=1;
this->mul(*this);
}
for(int i=0;i<n;i++)
for(int j=0;j<n;j++) a[i][j]=ret.a[i][j]%mod;
}
void display(){
for(int i=0;i<n;i++){
printf("%lld",a[i][0]);
for(int j=1;j<n;j++){
printf(" %lld",a[i][j]);
}
printf("\n");
}
}
};
int n=0,m=0;
char str[33];
int g[30][30]={0};
int next[33]={0};
void cal_next(){
next[1]=0;
next[0]=-1;
for(int i=2,j=0;i<=m;i++){
while(j>=0&&str[i]!=str[j+1]) j=next[j];
next[i]=++j;
}
}
int main(){
cin>>n>>m>>mod;
Matrix mk(m);
cin>>(str+1);
cal_next();
for(int i=0;i<m;i++){
for(char ch='0';ch<='9';ch++){
int j=i;
while(j>=0&&ch!=str[j+1]){
j=next[j];
}
g[i][j+1]++;
}
}
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
mk.a[i][j]=g[j][i];
}
}
mk.pow(n-1);
ll ret=0;
for(int i=0;i<m;i++){
ret+=9*mk.a[i][0]%mod+mk.a[i][1]%mod;
ret%=mod;
}
cout<<ret<<endl;
return 0;
}
本文介绍了一种用于生成GT考试准考证号的算法,该算法考虑了避免使用特定不吉利数字的需求,并通过动态规划和矩阵快速幂的方法解决了大规模数据下的计算问题。
330

被折叠的 条评论
为什么被折叠?



