[HNOI2008]GT考试

咕了一年的题(那个时候我刚打完 J 组,因为 NOIp 考了字符串于是开始学习 kmp),今天才写掉(话说那个时候我也不会矩乘)。

看到题不会,多半是 DP,然后发现 \(M\) 这么小,\(N\) 这么大,猜测矩乘。

这样我们就糊出了这道题,完结撒花。

考虑设计 \(\displaystyle f_{i,j}\) 表示考虑了前 \(i\) 位,目前与不吉利数字匹配了 \(j\) 位。

考虑转移,我们预处理一个数组 \(g_{i,j}\) 表示当匹配了 \(i\) 位时,要通过填一位数字变成匹配 \(j\) 的方案数。

这时候 \(f\) 的转移就出来了:\(\displaystyle f_{i,j}=\sum_{k=1}^{m-1} f_{i-1,k}\times g_{k,j}\)

然后就可以矩乘优化了。

那么 \(g_{i,j}\) 怎么处理?

通过 kmp 预处理出不幸运的串的 \(next\) 数组,每次就不断跳 \(next\) 知道 \(next\) 下一位等于加入的数为止,设跳到的位置为 \(j\),原本的位置为 \(i\)\(g_{i,j}\) 应该 \(+1\)

My Code
class matrix {
	private:
		int a[N][N];
	public:
		matrix() {memset(a,0,sizeof(a));}
		matrix(int sq[N][N]) {
			for(int i=0; i<=m; i++) {
				for(int j=0; j<=m; j++) {
					a[i][j]=sq[i][j];
				}
			}
		}
		void init() {
			for(int i=0; i<=m; i++) a[i][i]=1;
		}
		void mk(int x,int y,int v) {a[x][y]=v;}
		int calc(int nn) {
			int ret=0;
			for(int i=0; i<m; i++) (ret+=a[nn][i])%=Mod;
			return ret;
		}
		void print() {
			for(int i=0; i<=m; i++) {
				for(int j=0; j<=m; j++) {
					printf("%d ",a[i][j]);
				}
				puts("");
			}
		}
		matrix operator *(const matrix &bb) const {
			matrix ret;
			for(int i=0; i<=m; i++) {
				for(int j=0; j<=m; j++) {
					for(int k=0; k<=m; k++) {
						(ret.a[i][j]+=(a[i][k]*bb.a[k][j]))%=Mod;
					}
				}
			}
			return ret;
		}
} bs,f,zr;
matrix ksm(matrix a,int b) {
	// a.print();
	matrix c=zr;
	while(b) {
		if(b&1) c=c*a;
		a=a*a;
		b>>=1;
	}
	return c;
}
int main() {
	n=read(),m=read(),Mod=read();
	scanf("%s",c+1);
	int j=0;
	p[1]=0;
	for(int i=1; i<m; i++) {
		while(c[i+1]!=c[j+1]&&j) j=p[j];
		if(c[i+1]==c[j+1]) j++;
		p[i+1]=j;
	}
	for(int i=0; i<m; i++) {
		for(int j=0; j<=9; j++) {
			int pp=i;
			while(c[pp+1]!=(char)(j+'0')&&pp) pp=p[pp];
			if(c[pp+1]==(char)(j+'0')) pp++;
			g[i][pp]++;
			// cout<<i<<" "<<pp<<" "<<g[i][pp]<<endl;
		}
	}
	bs=matrix(g);
	zr.init();
	f.mk(0,0,1);
	f=f*ksm(bs,n);
	printf("%lld\n",f.calc(0)%Mod);
	return 0;
}
posted @ 2021-11-15 18:52  redproblemdog  阅读(34)  评论(0编辑  收藏  举报