BZOJ1009 - [HNOI2008]GT考试

原题链接

Description

给出一个长度为m(m20)的数字串,求不包含这个数字串的n(n109)位数字串有多少个。答案对k(k103)取模。

Solution

矩阵乘法优化DP。
f[i][j]表示末尾至少再补上j个数字才能形成模式串的i位数字串的个数。举例来说,模式串是12345,则f[i][1]表示以1234结尾的i位数字串的个数(再补一个数5),f[i][2]表示以123结尾的i位数字串的个数(再补两个数45)…
注意至少!比如对于模式串12123,结尾是1212的串应该只属于f[i][1]而不属于f[i][3]

那么,如何构造由[f[i][1]...f[i][m]]转移为[f[i+1][1]...f[i+1][m]]的矩阵呢?
i+1位数字串是由i位数字串在末尾加上一位数字得到的,所以我们可以通过枚举这一位上的数字来算出这个矩阵。再次举例,模式串121的转移矩阵为:

010011989
A11=0表示以12结尾的串,无论在末尾加上什么数也不能变成以12结尾的串。
A12=0表示以12结尾的串,无论在末尾加上什么数也不能变成以1结尾的串。
A13=9表示以12结尾的串,在末尾加上023456789这九个数能够变成不以112结尾的串。
A21=1表示以1结尾的串,在末尾加上2这个数能够变成以12结尾的串。
A22=1表示以1结尾的串,在末尾加上1这个数能够变成以1结尾的串。
A23=8表示以1结尾的串,在末尾加上03456789这八个数能够变成不以112结尾的串。

可以看到第一行元素和是9,其他行元素和是10。因为差1位就形成模式串的话,补上那位就不合法啦。
初始状态[00...1],再乘以转移矩阵Tn次方,答案就是最终得到的矩阵各元素之和。

时间复杂度O(m3logn)

Code

//[HNOI2008]GT考试
#include <cstdio>
#include <cstring>
int n,m,P; char s[30],s0[30];
struct matrix
{
    int row,col,v[30][30];
    matrix operator *(matrix b)
    {
        int p=row,q=col,r=b.col;
        matrix c; c.row=p,c.col=r;
        for(int i=1;i<=p;i++)
            for(int j=1;j<=r;j++)
            {
                c.v[i][j]=0;
                for(int k=1;k<=q;k++)
                    c.v[i][j]+=v[i][k]*b.v[k][j],c.v[i][j]%=P;
            }
        return c;
    }
}x,y;
int pre(int L)
{
    for(int i=1;i<=L;i++)
    {
        int j=i;
        while(s[j-i+1]==s0[j]&&j<=L) j++;
        if(j==L+1) return m-(L-i+1); 
    }
    return m;
}
matrix pow(matrix a,int b)
{
    matrix res; res.row=res.col=a.row;
    for(int i=1;i<=res.row;i++)
        for(int j=1;j<=res.col;j++)
            res.v[i][j]=(i==j);
    matrix t=a;
    while(b>0)
    {
        if(b&1) res=res*t;
        t=t*t; b>>=1;
    }
    return res;
}
int main()
{
    scanf("%d%d%d",&n,&m,&P);
    scanf("%s",s+1);
    x.row=x.col=m;
    for(int i=1;i<=m;i++)
    {
        memset(s0,0,sizeof s0);
        for(int j=1;j<=m-i;j++) s0[j]=s[j];
        for(char j='0';j<='9';j++) s0[m-i+1]=j,x.v[i][pre(m-i+1)]++;
    }
    x.v[1][0]=0;
    x=pow(x,n);
    y.row=1,y.col=m,y.v[1][m]=1; y=y*x;
    int ans=0;
    for(int i=1;i<=m;i++) ans+=y.v[1][i];
    printf("%d\n",ans%P);
    return 0;
}

P.S.

再这样咸鱼…会死…

posted @ 2018-01-26 17:37  VisJiao  阅读(112)  评论(0编辑  收藏  举报