[Nowcoder17797E][2021广东省赛] Excellent Number

题目链接

Solution

据信这是一类经典的用矩阵快速幂优化 $dp$ 的题目,学到了。

首先有一个显然的 $dp$:设 $dp[i][r][j]$ 表示已填前 $i$ 位、末尾与字符串 $k$ 匹配了 $j$ 位,当前模 $11$ 等于 $r$ 的方案数。那么转移就是:

其中 $x$ 是填在第 $i+1$ 位的数,$f(j,x)$ 是计算在匹配了 $j$ 位的情况下填上 $x$ 之后最多能匹配到的地方,可以用 fail 数组搞定。

但是直接转移是 $O(10\times 6\times 11\times n)\approx O(6.6e8)$,会超时。

然后这玩意儿可以矩阵快速幂优化,为了看的更清楚,我们可以滚掉 $dp$ 数组的第一维,然后发现,$dp$ 数组的转移是固定的,所以可以用矩阵表示出来。具体地说,由于 $(r,j)$ 能转移到 $((10r+x)\bmod 11,f(j,x))$,所以我们就在系数矩阵的第 $((10r+x)\bmod 11,f(j,x))$ 行第 $(r,j)$ 列加上 $1$(当然这里要把 $(r,j)$ 两个维度压扁成一个,按住不表),如此,前一个 $dp$ 数组乘上该矩阵就得到了后一个 $dp$ 数组。现在我就可以矩阵快速幂了。

计算答案的时候,用所有可能性 $10^n$ 减去不合法的数量 $\sum\limits_{r=1}^{10}\sum\limits_{j=0}^{lenk-1}dp[(r,j)]$,记得特判 $10^n$ 这个数。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const LL MOD = 998244353;

struct Matrix{
int n;
LL a[105][105];
Matrix(){ memset(a, 0, sizeof a); }
void eye(){
memset(a, 0, sizeof a);
for(int i = 1; i <= n; i++) a[i][i] = 1;
}
Matrix operator * (const Matrix &A) const{
Matrix c; c.n = n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
for(int k = 1; k <= n; k++)
(c.a[i][j] += a[i][k] * A.a[k][j] % MOD) %= MOD;
return c;
}
};

Matrix fpow(Matrix bs, LL idx){
Matrix res;
res.n = bs.n; res.eye();
while(idx){
if(idx & 1) res = res * bs;
bs = bs * bs;
idx >>= 1;
}
return res;
}

int n, lenk;
char k[200005];

int fail[105];
void getFail(char t[], int lent){
int i = 1, j = 0;
fail[1] = 0;
while(i <= lent){
if(!j || t[i] == t[j]){
i++, j++;
if(t[i] != t[j]) fail[i] = j;
else fail[i] = fail[j];
}
else j = fail[j];
}
}

inline int f(int j, int x){
j++;
while(j && (k[j] != x+'0'))
j = fail[j];
return j;
}

inline int get(int x, int y){ return x * (lenk + 1) + y + 1; }

int main(){
int T; for(scanf("%d", &T); T; T--){
memset(fail, 0, sizeof fail);
scanf("%d%s", &n, k+1);
lenk = strlen(k+1);
getFail(k, lenk);
Matrix mat;
mat.n = 100;
for(int j = 0; j < lenk; j++){
for(int x = 0; x <= 9; x++){
int g = f(j, x);
if(g == lenk) continue;
for(int r = 0; r < 11; r++)
(mat.a[get((r*10+x)%11, g)][get(r, j)] += 1) %= MOD;
}
}
mat = fpow(mat, n);
LL ans = 1;
for(int i = 1; i <= n; i++) (ans *= 10) %= MOD;
for(int r = 1; r < 11; r++)
for(int j = 0; j < lenk; j++)
(ans -= mat.a[get(r, j)][1]) %= MOD;
bool extra = (k[1] == '1');
for(int i = 2; i <= lenk; i++)
extra &= (k[i] == '0');
extra &= (n >= lenk - 1);
ans += extra;
ans = (ans % MOD + MOD) % MOD;
printf("%lld\n", ans);
}
return 0;
}
作者

xyfJASON

发布于

2021-07-04

更新于

2021-07-04

许可协议

评论