[HNOI2008]GT考试

题目描述

阿申准备报名参加 GT 考试,准考证号为 N 位数X1,X2Xn(0Xi9),他不希望准考证号上出现不吉利的数字。 他的不吉利数学A1,A2Am(0Ai9) 有 M位,不出现是指 X1,X2Xn 中没有恰好一段等于 A1,A2Am,A1X1 可以为 0

输入输出格式

输入格式:

 

第一行输入N,M,K.接下来一行输入M位的数

 

输出格式:

 

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

输入输出样例

输入样例#1: 
4 3 100
111
输出样例#1: 
81

说明

N≤10^9,M≤20,K≤1000

一些思路:

一道DP&&矩阵快速幂的题

首先,我们用DP式子表示出这道题所要求的值

设dp[i][j]表示X串前i位后缀匹配了A串的前j位,g[i][j]表示从匹配A串前i位变到前j位的方案数,没有就是0

易知,$dp[i][j]=sumlimits_{k=0}^{m-1} dp[i-1][k]*g[k][j]$

答案即为$sumlimits_{i=0}^{m-1} dp[n-1][i]$

我们会发现这是一个矩阵乘幂的式子

然后用矩阵快速幂乱搞一通即可

弱弱的说一句,其实我觉得这道题的预处理g[i][j]也蛮难的

上代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dwn(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int a[50][50],m,n,mod,k,zlk[50][50],ans[50][50],sum,haha;
char ch[50];
void check(int x,int y){
	if(x+1<y || (ch[x+1]==ch[y] && y<x+1)) return;
	rep(i,1,y-1){
		if(ch[i]!=ch[x-y+1+i]) return;
	}
	rep(i,y+1,m-1) if(a[x][i] && ch[i]==ch[y]) return;
	a[x][y]=1;
	return;
}
void ksm(){
	while(k){
		if(k&1){
			memset(zlk,0,sizeof(zlk));
			rep(i,0,m-1) rep(j,0,m-1) rep(k,0,m-1) zlk[i][j]+=ans[i][k]*a[k][j],zlk[i][j]%=mod;
			rep(i,0,m-1) rep(j,0,m-1) ans[i][j]=zlk[i][j];
		}
		memset(zlk,0,sizeof(zlk));
		rep(i,0,m-1) rep(j,0,m-1) rep(k,0,m-1) zlk[i][j]+=a[i][k]*a[k][j],zlk[i][j]%=mod;
		rep(i,0,m-1) rep(j,0,m-1) a[i][j]=zlk[i][j];
		k>>=1; 
	}
}
bool wk(int x,int y){
	rep(i,1,x+1){
		bool c=0;
		rep(j,1,i-1) if(ch[j]!=ch[x-i+j+1]){c=1; break;}
		if(!c && y+'0'==ch[i]) return 0;
	}
	return 1;
}
int main(){
    scanf("%d%d%d",&k,&m,&mod);
    cin>>ch+1;
    rep(i,1,m-2) dwn(j,m-1,1)  check(i,j);
    dwn(j,m-1,1){
	    check(m-1,j);
	    if(a[m-1][j] && ch[j]==ch[m]) a[m-1][j]=0;
    }
    dwn(i,m-1,0) rep(j,0,9)	if(wk(i,j)) a[i][0]++;
    a[0][1]=1;
    rep(i,0,m-1) ans[i][i]=1;
    ksm();
    rep(i,0,m-1) sum+=ans[0][i],sum%=mod;
    printf("%d",sum);
	return 0;
}

  

原文地址:https://www.cnblogs.com/handsome-zlk/p/10532872.html