【BZOJ】1009 [HNOI2008]GT考试 KMP+DP+矩阵优化

80 篇文章 0 订阅
30 篇文章 0 订阅

题目传送门

又是一道神题,感觉这题的思想好强啊。

首先考虑DP,定义f[i][j]表示当前我们DP到准考证的第i位,后缀与不吉利的数字匹配了j位的方案数。

状态转移方程为f[i][j]+=f[i-1][k],可以通过枚举第i位的数字,求出k的大小,进行状态的转移。

突然发现求k的过程和KMP的失配过程差不多,于是我们可以用KMP来转移状态。

但是这个DP显然是不符合我们的要求的,时间复杂度巨大。

考虑重构DP,定义f[i][j]表示当前我们DP到不吉利数字的前i位(注意,这里和上面不一样),在第i位后放置一个0~9的数字,新的数字与不吉利数字的匹配长度为j的方案数。

突然发现我们重构的DP构造出了一个m*m的矩阵,我们是不是可以用矩阵来优化这个DP呢?

观察数据范围,m<=20,O(m^3*log2(n))的时间复杂度可以满足我们的需求。OK,这题就这样愉快的被秒掉啦。

(好迷啊,为什么大佬们直接就可以想到矩阵快速幂的?一脸懵逼)

附上AC代码:

#include <cstdio>
#include <cstring>
using namespace std;

const int N=21;
int n,m,mod,nt[N],pre,sum;
char s[N];
struct note{
	int map[N][N],x,y;
	inline void clear(int l,int r){return (void)(x=l,y=r,memset(map,0,sizeof map));}
	inline friend note operator * (note p,note q){
		note sum;sum.clear(p.x,q.y);
		for (int i=0; i<=p.x; ++i)
			for (int j=0; j<=q.y; ++j)
				for (int k=0; k<=p.y; ++k)
					sum.map[i][j]=(sum.map[i][j]+p.map[i][k]*q.map[k][j])%mod;
		return sum;
	}
}a,ans;

inline note power(int m){
	note ret;ret.clear(n,n);
	for (int i=0; i<=n; ++i) ret.map[i][i]=1;
	while (m){
		if (m&1) ret=ret*a;
		m>>=1,a=a*a;
	}
	return ret;
}

int main(void){
	scanf("%d%d%d",&m,&n,&mod);
	scanf("%s",s+1);
	for (int i=2; i<=n; ++i){
		while (pre&&s[pre+1]!=s[i]) pre=nt[pre];
		if (s[pre+1]==s[i]) ++pre;
		nt[i]=pre;
	}
	a.clear(n,n);
	for (int i=0; i<n; ++i)
		for (int j=0; j<=9; ++j){
			pre=i;
			while (pre&&s[pre+1]-'0'!=j) pre=nt[pre];
			if (s[pre+1]-'0'==j) ++pre;
			if (pre<n) a.map[i][pre]=(a.map[i][pre]+1)%mod;
		}
	ans.clear(1,n),ans.map[0][0]=1,ans=ans*power(m);
	for (int i=0; i<n; ++i) sum+=ans.map[0][i];
	return printf("%d\n",sum%mod),0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值