gcd步数

题目描述

一个有趣的函数F(a,b),表示对于数对(a,b)调用辗转相除法的步数为多少

例如 (24,40)....0 (16,24).....1 (8,16).....2 (0,8)....3,即f(24,40)=3

现在已知f(a,b)=k,求(a,b)使得a+b尽量小,同时,由于最终的(a,b)可能比较大,所以你只要在模10^9+7同余系下输出结果即可

输入输出格式

输入格式:

一个数k

输出格式:

两个数a,b

输入输出样例

输入样例#1:
1000000007
输出样例#1:
0 1000000006

说明

对于100% 数据 k < 10^15

 

这个题很有意思,值得一想 

我们需要考虑gcd的过程 

最差情况是 -> 每次操作都是商1 -> 相当于两个数做差 

这个时候就是次数最多,也就是我们要找的最小的a,b的情况

由反复做差 -> 可以联想到斐波那契数列

经过简单的举例, 

这个题就是求斐波那契数列的第 k和k+1 项

(上面不太懂的话自己举个例推算一下就很好理解了)

由于数据范围很大,所以需要拿矩阵快速幂来算

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 110
#define mod 1000000007
using namespace std;
long long N,M;
struct Matrix{
    long long mat[MAXN][MAXN];
    
    Matrix operator *(const Matrix& a)
    {
        Matrix c;
        memset(c.mat,0,sizeof c.mat);
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                for(int k=1;k<=N;k++)
                    c.mat[i][j]=(c.mat[i][j]+mat[i][k]*a.mat[k][j])%mod;
        return c;
    };
    
    Matrix operator ^(long long k)
    {
        Matrix c=*this,t=*this;
        k--;
        for(;k;k>>=1,t=t*t)
            if(k&1)c=c*t;
        return c;
    };
    
    void print()
    {
        for(int i=1;i<=N;i++)
        {
            for(int j=1;j<=N;j++)printf("%d ",mat[i][j]);
            printf("\n");
        }
    };
    void scan()
    {
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                scanf("%lld",&mat[i][j]);
    };
};

int main()
{
    scanf("%lld",&M);M++;
    if(M==2){printf("1 1\n");return 0;}
    if(M>2)M-=1;
    else {printf("1");return 0;}
    Matrix x;
    N=2,x.mat[1][1]=1,x.mat[1][2]=1,x.mat[2][1]=1,x.mat[2][2]=0;
    Matrix p=x^M;
    printf("%lld ",p.mat[1][1]);
    p=x*p;
    printf("%lld\n",p.mat[1][1]);
    return 0;
}

 

posted @ 2017-10-19 21:03  Elfish?  阅读(171)  评论(0编辑  收藏  举报