[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;
}