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;
    }
    
  • 相关阅读:
    某个牛人做WINDOWS系统文件详解
    常用ASP脚本程序集锦
    LINUX基础:文件安全与权限
    proftpd+mysql+quota
    apache2.0.49tomcat5.0.19jk2建立virtualHost
    URL Redirection(转) Anny
    顶级域名后缀列表(转) Anny
    \u4E00\u9FA5意义 Anny
    How to POST Form Data Using Ruby(转) Anny
    How to get rid of 'Enter password to unlock your login keyring' in Ubuntu(转) Anny
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1009.html
Copyright © 2011-2022 走看看