20211117 rand
Description
\(T\leq 10 ^ 5 ;\ X ,\ N ,\ M \leq 10 ^ {18}\)
Analysis
容易发现,每次弹出来的 \(x\) 大概都是呈 \(fib_i \cdot x + fib_{i + 1} \cdot y\) 的样子,我们令 \(fib_0 = fib_1 = 1\) ,这样的话要注意 \(x\) 前面的系数都是偶数项的 \(fib\) , \(y\) 反之。
加上 \(fib\) 小于 \(10 ^ {18}\) 的项数只有 \(\log\) 级别个,这样就相当于我们用 exgcd 可以去解 \(\log\) 级别个式子形如 \(fib_i \cdot a + fib_{i + 1} \cdot b = X\) 。
Solution
接着 Analysis 的想法,发现题目里面有个要求是关于 \(a\) 和 \(b\) 的范围,要求 \(a \in [0, N] ,\ b \in [0, M]\) 因为任意两个 \(fib\) 的 gcd 均为一,所以我们如果已经解出来一个形如 \(fib_i \cdot a + fib_{i + 1} \cdot b = X\) 的式子,可以通过变形得到 \(fib_i \cdot (a - fib_{i + 1}) + fib_{i + 1} \cdot (b + fib_i) = X\) 从而去算其他的答案。
细节详见代码,那这样的话我们就相当于得到了一个近似 \(T\log^2\) 左右的代码,看上去是能过得,但是注意这里是值域的 \(\log\) ,所以大概率是过不了的,考虑怎么优化。
想到这么一个式子: \(fib_{i - 1} \cdot fib_{i + 1} = fib_i^2 - 1\) 可以稍微拆一下,可以大概用数学归纳法证明。
这样的话其实每次我们不用 exgcd 去求 \(a\) 和 \(b\) ,其中一种解都定好了,直接预处理就可以了。
所以 exgcd 的 \(\log\) 就去掉了,现在的话时间复杂度大概就是 \(T\log\) ,可以通过此题。
Code
/*
feibolacci 系数 exgcd
*/
#include <bits/stdc++.h>
using namespace std;
typedef __int128 ll;
ll X, n, m, a, b, fib[93], xs[93], ans;
inline ll read() {
ll s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
return s * w;
}
inline void write(ll x) {
if (!x) return ;
write(x / 10);
int num = x % 10;
printf("%d", num);
}
inline void mian() {
X = read(); n = read(); m = read(); ans = 0;
if (X == 0) {
printf("1\n");
return ;
}//
for (int i = 1; i <= 91; i += 2) {
if (fib[i] > X) continue;
a = -xs[i] * X; b = xs[i - 1] * X;
if (i == 1) a = 0, b = xs[i] * X;
ll num = 0, res = 0;
if (a < 0) {
num = (-a) / fib[i + 1];
a += num * fib[i + 1];
if (a < 0) a += fib[i + 1], ++num;
b -= num * fib[i];
if (b < 0) continue;
}
if (a > n) continue;
if (b > m) {
num = (b - m) / fib[i];
b -= num * fib[i];
if (b > m) b -= fib[i], ++num;
a += num * fib[i + 1];
if (a > n) continue;
}
if (b < 0) continue;//
num = (n - a) / fib[i + 1] + 1;
res = (b) / fib[i] + 1;
ans += min(res, num);
}
write(ans); printf("\n");
}
int main() {
// freopen("rand.in", "r", stdin);
// freopen("rand.out", "w", stdout);
fib[1] = fib[2] = xs[1] = xs[2] = xs[3] = 1;
fib[3] = 2;
for (int i = 4; i <= 92; ++i) {
fib[i] = fib[i - 1] + fib[i - 2];
xs[i] = xs[i - 1] + xs[i - 2];
}
int t = read();
while (t--) mian();
return 0;
}