http://www.elijahqi.win/archives/408
题目描述
阿申准备报名参加GT考试,准考证号为N位数X1X2….Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2…Am(0<=Ai<=9)有M位,不出现是指X1X2…Xn中没有恰好一段等于A1A2…Am. A1和X1可以为0
输入输出格式
输入格式:
第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000
输出格式:
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
输入输出样例
输入样例#1:
4 3 100
111
输出样例#1:
81
读这题加想题花了很久很久。。看了题解想也仍然想不懂。前几天和小可爱对赌了暑假作业,昨天一问发现我差了好多,于是年轻的我本想2小时搞定这题,但基础太差。中间出去吃个饭就从亮点到现在才搞定,面对作业甚是害怕
题解写的有点乱,慢慢看。
输出模K取余的结果. 一般来说可以判定是用矩阵倍增的方法了
f[i][j]表示在第i号时 后缀中有j位与不吉利的前缀j位相同 的个数有多少
如果仅仅这个定义那么应该是10的(i-j)次方种,但是我们要求前面同时也不能出现任何不吉利的数字 为了保证不重复,所有的f[i][j]都不包含 后缀中长度大于j且与不吉利数前缀的前j个相同的。反例:从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案 的情况
再zhx得指引下 先来理解好朴素dp 瞬间感觉自己渣渣 这dp都
f[0][0]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<m;j++){
for(int ch='0';ch<='9';ch++){
int k=j;
while(k>0&&s[k+1]!=ch) k=next[k]//加入新加入的不等于不吉利串中的新枚举的那个数,我们就不断倒kmp制作的Next数组
if(s[k+1]==ch) k++; 如果匹配成功则++但总存在完全不是不吉利串中的任何一位,那我们直接加就好了
if(k!=m) f[i+1][k]+=f[i][j];
}
}
}
写不好 想来是题目练习的太少了
按顺序处理准考证号每一位,
设f[i][j]表示:准考证号前i位中 后j位与不吉利数的前j位相同时,前i位的方案数
那么答案ans=f[n][0]+f[n][1]+…+f[n][m-1]
有人可能会想了,我中间任意几位和不吉利数字随便几位相同,那我也可能是答案啊
其实 这在递推的过程中已经解决 每次转移的时候f[i][j]只能由f[i-1][k]得到,相当于填完第i-1位后,将其后缀k(长为k的后缀)后面新添一位num,之后这个i位数的 与不吉利数前缀相同的最长后缀是:后缀j
i>=1时: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]
列出矩阵构造的答案
自己推导一下next数组配合程序看一下 样例123124 行表示原匹配长度 列表示新加一位匹配数变成列数的方案数
9 1 0 0 0 0
8 1 1 0 0 0
8 1 0 1 0 0
9 0 0 0 1 0
8 1 0 0 0 1
7 1 0 1 0 0
#include<cstdio>
#include<cstring>
#define N 30
struct matrix{
int f[N][N],l,c;
}base,base1;
int kk,n,m,next[N];
char a[N];
inline matrix multiply(matrix a,matrix b){
matrix c;c.l=a.l;c.c=b.c;memset(c.f,0,sizeof(c.f));
for (int i=1;i<=a.l;++i){
for (int j=1;j<=b.c;++j){
for (int z=1;z<=a.c;++z){
(c.f[i][j]+=((long long)a.f[i][z]*b.f[z][j]%kk))%=kk;
}
}
}
return c;
}
int main(){
//freopen("3193.in","r",stdin);
//freopen("3193.out","w",stdout);
scanf("%d%d%d",&n,&m,&kk);
scanf("%s",a);
int i=0,j=-1;next[0]=-1;
while (i<m){
if (j==-1||a[i]==a[j]){
++j;++i;next[i]=j;
}else j=next[j];
}
for (int i=m;i>=1;--i) a[i]=a[i-1];
base.c=m;base.l=m;base1.c=m;base1.l=m;base1.f[1][1]=1;
for (int i=0;i<m;++i){
for (int j='0';j<='9';++j){
int k=i;
while (k&&a[k+1]!=j) k=next[k];
if (a[k+1]==j) ++k;
if (k!=m) base.f[i+1][k+1]++; //避免构造由i个匹配变成m个匹配的个数
}
}
/* for (int i=1;i<=m;++i){
for (int j=1;j<=m;++j) printf("%d ",base.f[i][j]);printf("\n");
}*/
int k=n;
for (;k!=0;k>>=1,base=multiply(base,base)){
if (k&1) base1=multiply(base1,base);
}
int ans=0;
for (int i=1;i<=m;++i) (ans+=base1.f[1][i])%=kk;
printf("%d",ans);
return 0;
}