BZOJ 1009-GT考试(kmp+矩阵快速幂+DP)

来源:互联网 发布:linux 内核版本 编辑:程序博客网 时间:2024/06/08 10:18

1009: [HNOI2008]GT考试

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3921  Solved: 2393
[Submit][Status][Discuss]

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

HINT

Source


这道题着实让我搞了一下午,本来的第一感觉应该是数位DP的,但是n足足有10^9这么大,果断行不通啊。。。。

想了想感觉像是需要矩阵优化一发,推了一发递推式,后来感觉还是漏洞百出,无奈之下还是查看了题解。

题解也是看的我一脸懵逼,但是杠了将近一下午,总算是明白了不少。。。。

其实递推关系式还是很容易想到的,当前位一定只和前一位有关,可是如何能不重复的避免出现不幸运数字呢。。

这里参考的是一位大神的思路(仅供参考):

按顺序处理准考证号每一位,
设f[i][j]表示:准考证号前i位中 后j位与不吉利数的前j位相同时,前i位的方案数 
那么答案ans=f[n][0]+f[n][1]+…+f[n][m-1]

f[i][j]的准确含义:
1.f[i][j]表示的每种方案不仅与其后j位有关,还应保证不含不吉利数 
2.为避免重复,f[i][j]表示的每种方案都不含长度大于j且与不吉利数的前缀相同 的后缀 
 否则就会出现:从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案 的情况 

状态转移:
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]
        比如:还是假设不吉利数为123124,那么 f[i][3]=f[i-1][2]+f[i-1][5],因为 f[i-1][2]末尾的*****12不能是**12312,所以需要f[i-1][5]补充 
        但若不吉利数为123123,那么 f[i][3]=f[i-1][2],因为 f[i][3]末尾的*****123不能是**123123
i==0时:f[0][0]=1,f[0][其他]=0
其中,a[k][j]就表示上面提到的num能取几个值,可以用kmp算法预处理出来,它是一个矩阵 

这样就可以不重不漏地计数了 


再来个矩阵加速:f[i][j]求法是个线性齐次递推式,可以构造成矩阵 


#include<map>#include<stack>#include<queue>#include<vector>#include<math.h>#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>using namespace std;typedef long long  ll;#define  maxn 400004int k,n,m,p[maxn],hashs[maxn];char str[maxn];struct node{    int s[50][50];    node()    {        memset(s,0,sizeof(s));    }}a,z;node multi(node a,node b){    node res;    int i,j,kk;    for(i=0;i<m;i++)        for(j=0;j<m;j++)        {            for(kk=0;kk<m;kk++)                res.s[i][j]=(res.s[i][j]+a.s[i][kk]*b.s[kk][j])%k;            res.s[i][j]%=k;        }    return res;}node work(node x,int y){    int i,j;    node res;    for(i=0;i<m;i++)        for(j=0;j<m;j++)               if(i==j)                res.s[i][j]=1;    while(y)    {        if(y%2)            res=multi(res,x);        x=multi(x,x);        y/=2;    }    return res;}int  main(void){    int i,j,ans=0;    scanf("%d%d%d",&n,&m,&k);    scanf("%s",str+1);    p[1]=p[2]=1;    for(i=2;i<m;i++)    {        j=p[i];        while(j>1 && str[j]!=str[i])            j=p[j];        if(str[j]==str[i])            p[i+1]=j+1;        else            p[i+1]=1;    }    z.s[0][0]=1;    for(i=0;i<m;i++)    {        j=i+1;        int sum=a.s[i][j]=1;        hashs[str[j]]=i+1;        while(j!=1)        {            j=p[j];            if(hashs[str[j]]!=i+1)            {                a.s[i][j]=1;                hashs[str[j]]=i+1;                sum++;            }        }        a.s[i][0]=10-sum;    }    z=multi(z,work(a,n));    for(i=0;i<m;i++)        ans=(ans+z.s[0][i])%k;    printf("%d\n",ans);    return 0;}



原创粉丝点击