题意:
给定一个长度为
K
K
K ,首项为
A
A
A ,公差为
B
B
B 的等差数列
S
S
S,把这
K
K
K个数从小到大拼接到一起,形成一个新的数
N
N
N,求
N
%
M
N\%M
N%M的值。
思路:
设
b
i
t
(
i
)
bit(i)
bit(i) 表示第 i 项所需要进行的十进制位移。
那么根据题目所给的信息,
N
=
S
0
∗
1
0
b
i
t
(
0
)
+
S
1
∗
1
0
b
i
t
(
1
)
+
⋯
+
S
K
−
1
∗
1
0
b
i
t
(
K
−
1
)
N=S_{0}∗10^{bit(0)}+S_{1}∗10^{bit(1)}+⋯+S_{K-1}∗10^{bit(K-1)}
N=S0∗10bit(0)+S1∗10bit(1)+⋯+SK−1∗10bit(K−1) ,这么大的式子,就算是
p
y
py
py 暴力做也会超时。
然后,我们注意到,“等差数列中的所有项都小于 1 0 18 10^{18} 1018”,也就是说,等差序列中很多项的长度是相等的,而对于 b i t ( i ) bit(i) bit(i) 这个函数,有很多连续的项可以看成等差序列。于是我们可以按照位数给等差数列分组,最多可分 18 组。
接下来就具体看一个等差序列区间
[
L
,
R
]
[L,R]
[L,R] ,假定 每一项等差序列的长度均为
m
m
m 。
设这个区间上所表示的数为
F
(
m
)
F(m)
F(m) ,
F
(
m
)
F(m)
F(m) 中的每一项
f
i
=
S
i
∗
1
0
i
(
L
≤
i
≤
R
)
f_{i}=S_{i}*10^{i}(L\leq i\leq R)
fi=Si∗10i(L≤i≤R),与之相对应的:
F
(
m
)
=
∑
i
=
L
R
f
i
=
1
0
b
i
t
(
L
)
∗
∑
i
=
L
R
(
S
i
∗
1
0
(
R
−
i
)
∗
m
)
F(m)=\sum_{i=L}^Rf_{i}=10^{bit(L)}*\sum_{i=L}^R(S_{i}∗10^{(R-i)*m})
F(m)=∑i=LRfi=10bit(L)∗∑i=LR(Si∗10(R−i)∗m)。
经过一系列的代换,现在需要被解决的就只有
∑
i
=
L
R
(
S
i
∗
1
0
(
R
−
i
)
∗
m
)
\sum_{i=L}^R(S_{i}∗10^{(R-i)*m})
∑i=LR(Si∗10(R−i)∗m) 了。
设
r
e
t
(
j
)
=
∑
i
=
L
j
(
S
i
∗
1
0
(
j
−
i
)
∗
m
)
(
L
≤
j
≤
R
)
ret(j)=\sum_{i=L}^j(S_{i}∗10^{(j-i)*m})(L\leq j\leq R)
ret(j)=∑i=Lj(Si∗10(j−i)∗m)(L≤j≤R),这玩意其实就是
r
e
t
(
j
+
1
)
=
∑
i
=
L
j
(
S
i
∗
1
0
(
j
−
i
)
∗
m
)
∗
1
0
m
+
S
j
+
1
ret(j+1)=\sum_{i=L}^j(S_{i}∗10^{(j-i)*m})*10^{m}+S_{j+1}
ret(j+1)=∑i=Lj(Si∗10(j−i)∗m)∗10m+Sj+1。
这一坨真要一步一步搞也是个麻烦事,不如……我们把它变个样子:
设
r
e
t
(
L
−
1
)
=
0
ret(L-1)=0
ret(L−1)=0,构造如下系数矩阵
X
X
X 与矩阵
R
E
T
(
j
)
RET(j)
RET(j) :
X
=
[
1
0
m
0
0
1
1
0
0
B
1
]
,
R
E
T
(
j
)
=
[
r
e
t
(
j
)
S
j
+
1
1
]
X= \begin{gathered} \begin{bmatrix} 10^{m} & 0 &0 \\ 1 & 1 & 0\\ 0 & B & 1 \end{bmatrix} \end{gathered}, RET(j)= \begin{gathered} \begin{bmatrix} ret(j) & S_{j+1} & 1\end{bmatrix} \end{gathered}
X=⎣⎡10m1001B001⎦⎤,RET(j)=[ret(j)Sj+11]
于是:
R
E
T
(
j
)
=
R
E
T
(
j
−
1
)
∗
X
,
R
E
T
(
j
)
=
R
E
T
(
L
−
1
)
∗
X
j
−
L
+
1
RET(j)=RET(j−1)∗X,RET(j)=RET(L−1)∗X^{j−L+1}
RET(j)=RET(j−1)∗X,RET(j)=RET(L−1)∗Xj−L+1
通过矩阵快速幂,算出长度为
m
m
m 的一组值,最后再将每一组的结果相加即可。
时间复杂度:
O
(
O(
O(不会算
)
)
)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define VV vector<vector<int> >
#define init(x) vector<vector<int>> x(3, vector<int>(3, 0))
int K, A, B, mod, ans;
//inline void print(VV u) {
// for (int i = 0; i < 3; i++) {
// for (int j = 0; j < 3; j++) {
// cout << u[i][j] << " ";
// }
// cout << endl;
// }
//}
//矩阵乘法
VV mat_mul(VV a, VV b) {
init(res);
for (int k = 0; k < 3; k++)
for (int i = 0; i < 3; i++) {
if (a[i][k] == 0)
continue;
for (int j = 0; j < 3; j++)
res[i][j] = (res[i][j] + a[i][k] * b[k][j] % mod) % mod;
}
return res;
}
//快速幂
VV mat_pow(VV cnt, int k) {
init(res);
res[0][0] = res[1][1] = res[2][2] = 1;
while (k) {
if (k & 1)
res = mat_mul(res, cnt);
cnt = mat_mul(cnt, cnt);
k >>= 1;
}
return res;
}
// 从数列第 st 项开始,查找区间 [L, R],使得区间内的所有数都小于 bit 大于等于 bit/10
int st = 0, bit = 10, l, r;
bool get() {
if (st >= K || A + st * B >= bit)
return false;
l = st;
r = K - 1;
while (l < r) {
int mid = (l + r) >> 1;
if (A + mid * B < bit)
l = mid + 1;
else
r = mid;
}//二分查找区间右端点
if (A + r * B >= bit)
--r;
l = st;
st = r + 1; // 下一个起始位置
return true;
}
signed main() {
cin >> K >> A >> B >> mod;
//最多有18位
for (int i = 1; i <= 18; i++) {
if (get()) {
init(mat);
mat[0][0] = bit % mod;
mat[0][1] = mat[0][2] = mat[1][2] = mat[2][1] = 0;
mat[1][0] = mat[1][1] = mat[2][2] = 1;
mat[2][1] = B % mod;
mat = mat_pow(mat, r - l + 1);
vector<int> ret(3), tem(3);
ret[0] = ans;
ret[1] = (A + l * B) % mod;
ret[2] = 1;
tem[0] = tem[1] = tem[2] = 0;
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
tem[j] = (tem[j] + ret[k] * mat[k][j] % mod) % mod;
ans = tem[0];
}
bit *= 10;
}
cout << ans;
return 0;
}