gym 101512 BAPC 2014 I Interesting Integers (拓展欧几里得 + 斐波那契)

转载至:http://blog.csdn.net/HackerTom/article/details/78061843

Problem

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

Meaning

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

Analysis
 【数论】【扩展欧几里得】

  首先如果数列S是斐波那契数列,则A*S,S+S也满足f[i]=f[i-1]+f[i-2]。

  那么考虑A=1,B=0的斐波那契—卢卡斯数列S1,为第一个数对最终答案的影响。

  同样,A=0,B=1的斐波那契—卢卡斯数列S2,为第二个数对最终答案的影响。

  容易得到这两个数列是错位的斐波那契数列

  S1=1,0,1,1,2,3,5…

  S2=0,1,1,2,3,5,8…

  S2[i]=S1[i+1].

  而把S1*A+S2*B如果能含有N,则A B的最小解即为所求。

  所以只需要求出斐波那契数列的前45项(109内),接下来就是枚举N是由斐波那契数列中哪两个相邻的数分别乘A和B得到的。

  即A*f[i]+B*f[i-1]=N。可以对f[i],f[i-1]扩展欧几里得,求出对应的A和B,看看能否把X,Y调成满足题意得(0

#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]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值