NAQ 2022
B. Birthday Gift
数位dp + 矩阵快速幂优化 + 一个比较tricky的同余优化,卡时间
credit:学姐给我讲的思路和官方solution的思路 QwQ
我太菜了,脑子在构造矩阵那里卡住了(说明不要上课走神的时候想题,容易把自己带进不存在的沟里)。后面具体实现的时候不断地犯一些愚蠢错误(范围,条件,etc.)本来想 2250 * 2250 用小数据测试一下矩阵写得对不对,但是空间卡住了只能硬上最终解法。学长后来说是结构体里放数组的原因,看来要update一下板子了(
Prompt
- Construct an integer number that
- has
adigits % 225 == b- adjacent digits not identical
- has
- How many ways?
Constraints
- $1 \leq a \leq 10^{18}$
- $0\leq b< 225$
Reason
- naive: 数位dp,从个位开始构造
i =当前数字多少位,j =当前个位是几,k =当前数字除以\(225\)的余数0 <= p <= 9 && p != jdp[i+1][p][(k * 10 + p) % 225] += dp[i][j][k]- edge case: 注意
dp[0][0]= 1 但转移的时候不能从0开始转,因为0会变成leading zero - 问题:超大时
- better: 把转移方程变成矩阵,用矩阵快速幂加速
- 问题:
2250 * 2250的矩阵乘法还是会超时
- 问题:
- final:
- 注意到 \(225=25*9=lcm(25,9)\),因此
- 而\(25\)的余数只和最后两位有关
- 所以我们只需要记录
k =当前数字除以\(9\)的余数,再手动check一下最后两位除以\(25\)的余数就可以了,这样就变成90 * 90的矩阵乘法,可以接受
Solution
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <algorithm>
#include <iostream>
#include <map>
#include <numeric>
#include <set>
#include <string>
#include <vector>
using namespace std;
#define PII pair<int, int>
#define LL long long
const LL MOD = 1e9 + 7;
const int N = 90;
const int K = 9;
struct Matrix {
LL a[N][N];
Matrix() {
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
a[i][j] = 0;
}
void display() const {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++)
cout << a[i][j] << " ";
cout << endl;
}
}
inline Matrix operator*(const Matrix &b) const {
Matrix res;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
for (int k = 0; k < N; ++k)
res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % MOD;
return res;
}
inline Matrix operator^(LL x) const {
Matrix res, bas;
for (int i = 0; i < N; ++i)
res.a[i][i] = 1;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
bas.a[i][j] = a[i][j] % MOD;
while (x) {
if (x & 1)
res = res * bas;
bas = bas * bas;
x >>= 1;
}
return res;
}
};
void solve() {
LL a, b;
cin >> a >> b;
vector<LL> init(N, 0);
if (a < 2) {
cout << (b < 10 ? 1 : 0) << endl;
return;
}
for (int i = 1; i < 10; i++)
init[(i % 9) * 10 + i] = 1;
auto mat = Matrix();
for (int j = 0; j < 10; j++)
for (int k = 0; k < K; k++)
for (int p = 0; p < 10; p++) {
int q = (k * 10 + p) % K;
if (j != p)
mat.a[q * 10 + p][k * 10 + j] += 1;
}
auto f = mat ^ (a - 2);
// f.display();
vector<LL> res(N, 0);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++) {
res[i] += f.a[i][j] * init[j];
res[i] %= MOD;
}
LL s = 0;
for (int j = 0; j < 10; j++)
for (int p = 0; p < 10; p++) {
if ((j * 10 + p) % 25 != b % 25 || j == p)
continue;
for (int k = 0; k < K; k++) {
int q = (k * 10 + p) % K;
if (q == b % K) {
s += res[k * 10 + j];
s %= MOD;
}
}
}
cout << s << endl;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
solve();
return 0;
}
本文由作者按照
CC BY 4.0
进行授权