gym 101512 BAPC 2014 I Interesting Integers

来源:互联网 发布:js 滚动加载更多 编辑:程序博客网 时间:2024/06/06 17:39

Problem

codeforces.com/gym/101512/attachments
vjudge.net/contest/186506#problem/I

Meaning

给出一个 正整数 n,要找尽量小的 a 和 b(a < b),使得 n 是以 a 和 b 作为头两项的斐波那契数列的某一项。

Analysis

当 a = b = 1 时,数列增长得最慢,然而即使这样,第 45 项也已经超过 109,意味着 n 项数在 45 以内。
对于斐波那契数列的第 k 项,可以把它写成头两项的线性表达式:fib[k] = c1 * fib[1] + c2 * fib[2]。而当项数 k 固定下来,系数c1 和 c2 也就是固定的常数了(可以从用矩阵快速幂求斐波那契数列第 k 项的做法理解)。
列出斐波那契数列前几条递推式,不难发现系数 c1 和 c2 规律:c1 = fib[k-2]c2 = fib[k-1],所以有:fib[k] = fib[k-2] * f[1] + fib[k-1] * fib[2]
枚举项数 k,求解c1 * x + c2 * y = n,相当于找直线上的整点。其中 x 和 y 分别就是所求的 a 和 b 的候选解。
因为要满足 a > 0、b > 0、a < b,而求出的 x 和 y 不一定满足这三个条件,要先做一些偏移,使整点(x,y)移动到直线y = x上方,然后再更新答案。

Code

#include <iostream>using namespace std;const int N = 1e9, TERM = 45;int fib[TERM+1];void table(){    fib[0] = 0;    fib[1] = 1;    for(int i = 2; i <= TERM; ++i)        fib[i] = fib[i-1] + fib[i-2];}long long extgcd(long long a, long long b, long long &x, long long &y){    long long d = a;    if(b)    {        d = extgcd(b, a % b, y, x);        y -= (a / b) * x;    }    else    {        x = 1;        y = 0;    }    return d;}int main(){    ios::sync_with_stdio(false);    cin.tie(0);    table();    int T;    cin >> T;    while(T--)    {        int n;        cin >> n;        int a = 1, b = n - 1;        for(int t = TERM; t > 2; --t)        {            long long x, y, k,                // x 的系数、y 的系数                fa = fib[t-2], fb = fib[t-1],                // 求解 fa * x + fb * y = gcd(fa, fb)                g = extgcd(fa, fb, x, y);            // gcd(fa, fb) 不整除 n            // 则方程无解            if(n % g)                continue;            // 方程两边同乘 n / gcd(fa, fb)            // 即得 fa * x + fb * y = n 的解 (x,y)            x *= n / g;            y *= n / g;            // 整点移动时的增量            // x 的增量是 fb            // y 的增量是 fa            fa /= g;            fb /= g;            // 把整点移到 y = x 上方            // 即 x - k * fb <= y + k * fa            if(x > y)            {                k = (x - y + fa + fb - 1) / (fa + fb);                x -= k * fb;                y += k * fa;            }            // 保证整点在 y = x 上方的前提下            // 尽量地往下移,以得到最小的 y            // y - k * fa >= x + k * fb            k = (y - x) / (fa + fb);            x += k * fb;            y -= k * fa;            // 更新答案            if(x > 0 && y > 0)            {                if(b > y)                    a = x, b = y;                else if(b == y && a > x)                    a = x, b = y;            }        }        cout << a << ' ' << b << '\n';    }    return 0;}

Post Script

fib[1] = a
fib[2] = b
fib[3] = a + b
fib[4] = fib[2] + fib[3] = a + 2b
fib[5] = fib[3] + fib[4] = 2a + 3b
fib[6] = fib[4] + fib[5] = 3a + 5b
fib[7] = fib[5] + fib[6] = 5a + 8b
……
fib[n] = fib[n-2] * a + fib[n-1] * b
fib[n] = fib[n-2] * fib[1] + fib[n-1] * fib[2]

原创粉丝点击