1 条题解
-
0
自动搬运
来自洛谷,原作者为

IsoTls
**搬运于
2025-08-24 21:38:52,当前版本为作者最后更新于2020-05-02 13:13:21,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
很明显,这个题目只要确定了每一行的第一个数,该行所有的数就都确定了。 在数学上,每个正整数都可以写成若干个不重复的斐波那契数之和。为了证明这个结论,先证明引理一。
引理一: 如果已知某个斐波那契数为 ,下一个斐波那契数为 ,那么
证明:
,(1)
,(2)
结合(1)(2)得证。
定理:每个正整数都可以写成若干个不重复的斐波那契数之和。
可以用数学归纳法证明:
(1)当 时, 显然成立。
(2)当 时,假设对于所有小于 的正整数,都成立。
如果 本身就是斐波那契数,那么显然 成立。
如果 不是斐波那契数,我们可以找到小于 的最大斐波那契数 。由归纳假设可知 可以写成若干不重复的斐波那契数之和。由引理一可知,在 和 之间必然存在另外一个斐波那契数。既然 是小于 的最大斐波那契数, 和 必然满足关系: ,因此 。而 ,所以 也可以写成若干不重复的斐波那契数之和。
综合(1),(2)得证。
再加上零,这样我们就可以把每个整数写成斐波那契进制。具体的做法是将整数 写成若干斐波那契数之和,把对应斐波那契数的位置填 ,其他位置填 。如以下列表:
如果限定没有相邻的 ,这种表示法是唯一的。
既然题目中给出的表和斐波那契数有关,如果我们把表中每个整数转换成斐波那契进制,说不定会有什么发现。转换之后如下
行号 第一列 第二列 第三列 第四列 第五列 1 001 10 100 1000 10000 2 101 1010 10100 101000 1010000 3 1001 10010 100100 1001000 10010000 4 10001 100010 1000100 10001000 100010000 5 10101 101010 1010100 10101000 101010000 6 100001 1000010 10000100 100001000 1000010000 7 100101 1001010 10010100 100101000 1001010000 8 101001 1010010 10100100 101001000 1010010000 9 1000001 10000010 100000100 1000001000 10000010000 10 1000101 10001010 100010100 1000101000 10001010000 11 1001001 10010010 100100100 1001001000 10010010000 先观察每一行,可以发现,每行第一列的最末位都是 。将第一列的数字左移一位后得到第二列,左移两位后得到第三列,以此类推。
再观察第一列,因为最末位是 ,而斐波那契进制要求不能有连续的1,所以第一列的数字形式为 ,这为我们计算第一列的数提供了方便。又由于第一列的数是最小的之前从未出现过的数,所以把行号减一转换成斐波那契数进制即可。
比如要求第 行的第一个数,可以将 转换为斐波那契进制为:,后面再补一个 即为:
这样就求得了第 行的第一个数,然后再通过左移 次,可以得到第 行,第 列的数。
代码如下:
#include <stdio.h> #define MAX 1000000000 #define SIZE 256 int bits[SIZE]; int fib[SIZE] = {1, 1, 2}; int fib_len; int shifted[SIZE]; struct Matrix { int m[2][2]; Matrix() { m[0][0] = 1; m[0][1] = 1; m[1][0] = 1; m[1][1] = 0; } }; int find_lower(int value) { int low = 0; int high = fib_len - 1; int mid; while (low <= high) { mid = (low + high) / 2; if (fib[mid] <= value) { low = mid + 1; } else { high = mid - 1; } } return low - 1; } int fill_bits(int value) { bits[0] = 1; bits[1] = 0; value--; int len = find_lower(value); int k = len; while (k > 0 && value > 0) { if (value >= fib[k]) { bits[k+1] = 1; value -= fib[k]; } k--; } return len + 2; } Matrix matrix_mul(const Matrix* a, const Matrix* b, int M) { Matrix c; c.m[0][0] = (a->m[0][0] * b->m[0][0] + a->m[0][1] * b->m[1][0]) % M; c.m[0][1] = (a->m[0][0] * b->m[0][1] + a->m[0][1] * b->m[1][1]) % M; c.m[1][0] = (a->m[1][0] * b->m[0][0] + a->m[1][1] * b->m[1][0]) % M; c.m[1][1] = (a->m[1][0] * b->m[0][1] + a->m[1][1] * b->m[1][1]) % M; return c; } Matrix matrix_pow(const Matrix* matrix, int n, int M) { if (n == 1) { return {}; } Matrix t = matrix_pow(matrix, n/2, M); t = matrix_mul(&t, &t, M); if (n & 1) { return matrix_mul(&t, matrix, M); } return t; } void get_shifted_fib(int j, int k, int M) { if (j == 0) { shifted[0] = 1 % M; shifted[1] = 2 % M; } else { Matrix c; c = matrix_pow(&c, j, M); shifted[0] = (c.m[1][0] * 2 + c.m[1][1]) % M; shifted[1] = (c.m[0][0] * 2 + c.m[0][1]) % M; } for (int i = 2; i <= k; i++ ) { shifted[i] = (shifted[i-1] + shifted[i-2]) % M; } } int main() { int i, j, m, k; for (k = 2; fib[k-1] < MAX; k++) { fib[k] = fib[k-1] + fib[k-2]; } fib_len = k - 1; scanf("%d%d%d", &i, &j, &m); k = fill_bits(i); // 计算第 i 行,第一列的数 get_shifted_fib(j-1, k, m); // 左移 j-1 位 int ans = 0; for (i = 0; i <= k; i++) { if (bits[i]) { // 按位计算 ans = (ans + shifted[i]) % m; } } printf("%d\n", ans); return 0; }
- 1
信息
- ID
- 1584
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者