20211117 rand

Description

image

\(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;
}
posted @ 2021-11-17 19:00  Illusory_dimes  阅读(25)  评论(0编辑  收藏  举报