zoukankan      html  css  js  c++  java
  • 【BZOJ1009】[HNOI2008] GT考试(KMP+矩阵乘法)

    点此看题面

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

    矩阵乘法

    看到数据范围里(nle10^9,mle20),第一反应便是矩阵乘法......

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

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

    考虑求出当匹配至(m)位数的第(i(0le 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;
    }
    
  • 相关阅读:
    JSP一个简单的项目实现教程
    多个Excel文件快速导入到DB里面
    NotePad++左侧导航
    简单实用JSTL标签库
    Eclipse导入现有项目
    Java工具Eclipse
    winform窗体只能放大不能缩小
    ref out

    数组
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1009.html
Copyright © 2011-2022 走看看