1 条题解

  • 0
    @ 2025-8-24 21:38:52

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar IsoTls
    **

    搬运于2025-08-24 21:38:52,当前版本为作者最后更新于2020-05-02 13:13:21,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    很明显,这个题目只要确定了每一行的第一个数,该行所有的数就都确定了。 在数学上,每个正整数都可以写成若干个不重复的斐波那契数之和。为了证明这个结论,先证明引理一。

    引理一: 如果已知某个斐波那契数为 FnF_n,下一个斐波那契数为 Fn+1F_{n+1},那么 Fn<Fn+1<2FnF_n < F_{n+1} < 2*F_n

    证明:

    Fn<Fn+Fn1=Fn+1F_n < F_n + F_{n-1} = F_{n+1} ,(1)

    Fn+1=Fn+Fn1<Fn+Fn=2FnF_{n+1} = F_n + F_{n-1} < F_n + F_n = 2*F_n,(2)

    结合(1)(2)得证。

    定理:每个正整数都可以写成若干个不重复的斐波那契数之和。

    可以用数学归纳法证明:

    (1)当 N=1N=1 时,1=11 = 1 显然成立。

    (2)当 N>1N > 1时,假设对于所有小于 NN 的正整数,都成立。

    如果 NN 本身就是斐波那契数,那么显然 N=NN = N 成立。

    如果 NN 不是斐波那契数,我们可以找到小于 NN 的最大斐波那契数 KK。由归纳假设可知 NKN - K 可以写成若干不重复的斐波那契数之和。由引理一可知,在 KK2K2*K 之间必然存在另外一个斐波那契数。既然 KK 是小于 NN 的最大斐波那契数,NNKK 必然满足关系:N<2K N < 2*K ,因此 NK<K N-K < K。而 N=K+(NK)N = K + (N - K),所以 NN 也可以写成若干不重复的斐波那契数之和。

    综合(1),(2)得证。

    再加上零,这样我们就可以把每个整数写成斐波那契进制。具体的做法是将整数 NN 写成若干斐波那契数之和,把对应斐波那契数的位置填 11 ,其他位置填 00。如以下列表:

    0=00 = 0

    1=11 = 1

    2=102 = 10

    3=1003 = 100

    4=3+1=1014 = 3 + 1 = 101

    5=10005 = 1000

    6=5+1=10016 = 5 + 1 = 1001

    7=5+2=10107 = 5 + 2 = 1010

    8=100008 = 10000

    9=8+1=100019 = 8 + 1 = 10001

    ......

    如果限定没有相邻的 11 ,这种表示法是唯一的。

    既然题目中给出的表和斐波那契数有关,如果我们把表中每个整数转换成斐波那契进制,说不定会有什么发现。转换之后如下

    行号 第一列 第二列 第三列 第四列 第五列
    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

    先观察每一行,可以发现,每行第一列的最末位都是 11。将第一列的数字左移一位后得到第二列,左移两位后得到第三列,以此类推。

    再观察第一列,因为最末位是 11,而斐波那契进制要求不能有连续的1,所以第一列的数字形式为 01------01,这为我们计算第一列的数提供了方便。又由于第一列的数是最小的之前从未出现过的数,所以把行号减一转换成斐波那契数进制即可。

    比如要求第 1010 行的第一个数,可以将 99 转换为斐波那契进制为:1000110001,后面再补一个 0101 即为:10001011000101

    这样就求得了第 ii 行的第一个数,然后再通过左移 j1j-1 次,可以得到第 ii 行,第 jj 列的数。

    代码如下:

    #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
    上传者