首页 > 其他 > 详细

【BZOJ1009】[HNOI2008] GT考试(KMP+矩阵乘法)

时间:2020-02-01 11:16:26      阅读:63      评论:0      收藏:0      [点我收藏+]

点此看题面

大致题意: 求有多少个\(n\)位数(可以有前导\(0\)),满足其中不存在一个给定的\(m\)位数字(可以有前导\(0\))。

矩阵乘法

看到数据范围里\(n\le10^9,m\le20\),第一反应便是矩阵乘法......

我们设\(f_{x,y}\)表示\(n\)位数的第\(x\)位匹配至\(m\)位数的第\(y\)\(n\)位数中不存在\(m\)位数的方案数。

然后,我们通过\(KMP\),求出\(m\)位数的\(nxt\)数组。

考虑求出当匹配至\(m\)位数的第\(i(0\le i<m)\)位时,加上一个数后匹配至另外某一位的方案数。

于是,我们初始化\(j=i\),然后每次操作结束后令\(j=nxt[j]\)

\(i\)位能转移至第\(j+1\)位,当且仅当\(m\)位数中的第\(j+1\)位不同于之前出现过的任意一位,否则在加上这个数后就不会转移到第\(j+1\)位而是转移到第一次出现这个数的位置。

所以,我们可以用一个变量\(fg\)来状压存储下每个数字是否出现过,并记录下数字的个数\(p\)

则,第\(i\)位转移至第\(0\)位(即完全不能匹配)的方案数为\(10-p\)

于是我们就能借此求出转移矩阵了。

初始化\(f_{0,0}=1\),通过矩阵乘法我们得到\(f_n\),而答案就是\(\sum_{i=0}^{m-1}f_{n,i}\)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 20
using namespace std;
int n,m,X;char s[M+5];
namespace KMP
{
    int nxt[M+5];
    I void GetNxt()//求出nxt数组
    {
        RI i,j;for(i=2,nxt[1]=j=0;i<=m;++i)
            {W(j&&s[j+1]^s[i]) j=nxt[j];s[j+1]==s[i]&&++j,nxt[i]=j;}
    }
}
class Mat
{
    private:
        int n,m,a[M+5][M+5];
    public:
        I Mat(CI x=0,CI y=0):n(x),m(y){memset(a,0,sizeof(a));}
        I int* operator [] (CI x) {return a[x];}
        I Mat operator * (Con Mat& o) Con//矩阵乘法
        {
            Mat t(n,o.m);int i,j,k;for(i=0;i<=t.n;++i) for(j=0;j<=t.m;++j)
                for(k=0;k<=m;++k) t[i][j]=(1LL*a[i][k]*o.a[k][j]+t[i][j])%X;return t;
        }
        I Mat operator ^ (RI y) Con//矩阵快速幂
        {
            Mat x=*this,t(n,m);for(RI i=0;i<=n;++i) t[i][i]=1;
            W(y) y&1&&(t=t*x,0),x=x*x,y>>=1;return t;
        }
}S,U;
int main()
{
    RI i,j;scanf("%d%d%d%s",&n,&m,&X,s+1),KMP::GetNxt();
    RI v,p,fg;for(U=Mat(m,m),i=0;i^m;++i)//求出转移矩阵
    {
        p=fg=0,j=i;W(v=s[j+1]&15,!((fg>>v)&1)&&(++p,fg|=1<<v,U[i][j+1]=1),j) j=KMP::nxt[j];//不断枚举nxt,每种数字只能转移一次
        U[i][0]=(10-p)%X;//求出转移到第0位的方案数
    }
    RI t=0;for(S=Mat(0,m),S[0][0]=1,S=S*(U^n),i=0;i^m;++i) (t+=S[0][i])>=X&&(t-=X);//矩乘之后统计答案
    return printf("%d",t),0;
}

【BZOJ1009】[HNOI2008] GT考试(KMP+矩阵乘法)

原文:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1009.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!