zoukankan      html  css  js  c++  java
  • 【刷题】BZOJ 3262 [HNOI2008]GT考试

    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

    Solution

    这题还是很无奈的

    按照数位dp的思想依次考虑每一位上的数字,考虑设计dp,(f[i][j]) 代表考虑完第 (i) 位之后,后 (j) 位与不吉利数字的前 (j) 位相同的方案数

    那么最后答案为 (ans=sum_{i=0}^{m-1}f[n][i])

    现在新加进来一个数,对之前的相同的 (j) 位造成的影响会有三种情况:

    1. 延长原有的 (j) 位,变成 (j+1)

    2. 直接把原有的 (j) 位打回0位

    3. 把原来的 (j) 位变短到一个位置,而这个位置,你会发现正好是KMP中求的fail/next数组

    那么,(f[i][j]=f[i-1][j-1]+sum_{k=1}^mf[i-1][k]*[next[k]=j-1])

    再进一步,(f[i][j]=sum_{k=0}^{m-1}f[i-1][k]*a[k][j]),其中 (a[k][j]) 代表从匹配好 (k) 位变成匹配 (j) 位的方案数

    对于 (a) 数组,先KMP求出fail/next数组,然后直接枚举每个位置上的每个数,相当于暴力求得

    对于 (f) 数组,看上面的式子难道不眼熟吗,这东西显然可以矩阵快速幂优化

    然后答案就出来了

    注意一下每个人的KMP写法都不一样,求的fail/next数组也会有所不同,但只要能达到效果就可以了,不同的写法最后算 (a) 的时候,细节略有不同

    #include<bits/stdc++.h>
    #define ll long long
    #define db double
    #define ld long double
    const int MAXM=400+5;
    int n,m,Mod,ans,nexts[MAXM];
    char s[MAXM];
    struct Matrix{
    	int a[MAXM][MAXM];
    	inline void init()
    	{
    		memset(a,0,sizeof(a));
    	}
    	inline Matrix operator * (const Matrix &A) const {
    		Matrix B;
    		for(register int i=0;i<m;++i)
    			for(register int j=0;j<m;++j)
    			{
    				B.a[i][j]=0;
    				for(register int k=0;k<m;++k)(B.a[i][j]+=(a[i][k]*A.a[k][j]))%=Mod;
    			}
    		return B;
    	};
    };
    Matrix A,B;
    template<typename T> inline void read(T &x)
    {
    	T data=0,w=1;
    	char ch=0;
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    	x=data*w;
    }
    template<typename T> inline void write(T x,char c='')
    {
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    	if(c!='')putchar(c);
    }
    template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
    template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
    template<typename T> inline T min(T x,T y){return x<y?x:y;}
    template<typename T> inline T max(T x,T y){return x>y?x:y;}
    inline void KMP()
    {
    	nexts[0]=-1;
    	for(register int i=1;i<m;++i)
    	{
    		int j=nexts[i-1];
    		while(s[i]!=s[j+1]&&j>=0)j=nexts[j];
    		if(s[i]==s[j+1])nexts[i]=j+1;
    		else nexts[i]=-1;
    	}
    }
    inline void init()
    {
    	KMP();
    	for(register int i=0;i<m;++i)
    		for(register int j='0';j<='9';++j)
    		{
    			int k=i;
    			while(k&&s[k]!=j)k=nexts[k-1]+1;
    			if(s[k]==j)k++;
    			if(k!=m)B.a[i][k]++;
    		}
    }
    inline Matrix Fast_Matrix(int k)
    {
    	Matrix res;
    	res.init();
    	res=B;
    	--k;
    	while(k)
    	{
    		if(k&1)res=res*B;
    		B=B*B;
    		k>>=1;
    	}
    	return res;
    }
    int main()
    {
    	read(n);read(m);read(Mod);
    	A.init();B.init();
    	scanf("%s",s);
    	init();
    	A.a[0][0]=1;
    	B=Fast_Matrix(n);
    	A=A*B;
    	for(register int i=0;i<m;++i)(ans+=A.a[0][i])%=Mod;
    	write(ans,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    最后一周作业
    第十四,十五周作业
    第七周作业
    第六周作业
    第四周作业
    第三周作业
    第二周作业
    二学期第三次作业
    二学期第二次作业
    二学期第一次作业
  • 原文地址:https://www.cnblogs.com/hongyj/p/8671892.html
Copyright © 2011-2022 走看看