题解 P3216 【[HNOI2011]数学作业】
这一道题
……
\[n\leq 10^{18}
\]
这一看就要用矩阵乘法
我们先写出递推式
\[f(n+1)=f(n)\times 10^{calc(n+1)}+(n+1)
\]
这其中,calc代表的是n+1在十进制中有多少位
既然有calc,那么我们自然而然得想到划分
因为数位是连续的,所以将(19),(1099),(100~999)……(1e17+1,1e18-1)划分成区间
这时,calc就可以当成定值了
然后我们就可以写出矩形递推式了
\[\begin{bmatrix}f_{n+1}\\n+2\\1\end{bmatrix}
=
\begin{bmatrix}
calc&\;1&\;0
\\
0 &\;1&\;1
\\
0&\;0&\;1
\end{bmatrix}
\times
\begin{bmatrix}f_{n}\\n+1\\1\end{bmatrix}
\]
然后时间复杂度就是
\[O(log^3(n))
\]
解决
#include<cstdio>
#define Starseven main
#define ll long long
ll mod, S[20];
struct matrix {
ll va[4][4];
int line, cross;
void Mem() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
va[i][j] = 0;
}
}
return ;
}
};
matrix operator *(const matrix &a, const matrix &b) {
matrix c;
c.Mem();
for (int i = 1; i <= a.line; i++) {
for (int k = 1; k <= a.cross; k++) {
for (int j = 1; j <= b.cross; j++) {
c.va[i][j] = (c.va[i][j] + a.va[i][k] * b.va[k][j] % mod) % mod;
}
}
}
c.line = a.line;
c.cross = b.cross;
return c;
}
int Starseven(void) {
ll n;
read(n);
read(mod);
matrix ans, txt;
ans.Mem();
ans.va[1][1] = 1;
ans.va[2][1] = 2;
ans.va[3][1] = 1;
ans.line = 3;
ans.cross = 1;
ll hack = 1;
for (int i = 1; i <= 18; i++) {
S[i] = hack * 10;
S[i] -= hack;
hack *= 10;
}
hack = 10;
for (ll i = 1; i <= 18; i++) {
txt.Mem();
txt.va[1][1] = hack % mod;
txt.va[1][2] = 1;
txt.va[2][2] = 1;
txt.va[2][3] = 1;
txt.va[3][3] = 1;
txt.line = 3;
txt.cross = 3;
if(n >= S[i]) {
ll b = S[i];
if(i == 1) b -= 1;
while(b) {
if(b & 1ll) ans = txt * ans;
txt = txt * txt;
b >>= 1ll;
}
n -= S[i];
}
else {
if(i == 1) n -= 1;
while(n) {
if(n & 1ll) ans = txt * ans;
txt = txt * txt;
n >>= 1ll;
}
break;
}
hack = (hack * 10) % mod;
}
write(ans.va[1][1]);
puts("");
return 0;
}