3 条题解

  • 2
    @ 2017-03-17 15:47:51

    广义斐波那契数列 F[x]=F[x-1]+F[x-2] 可以写成 F[x]=a*F[1]+b*F[2] 的形式
    写一个暴力程序算一下a和b

    int a=0,b=0;
    void F(int n){
        if(n==1)a++;
        else if(n==2)b++;
        else F(n-1),F(n-2);
    }
    

    F[3]=1*F[1]+1*F[2]
    F[4]=1*F[1]+2*F[2]
    F[5]=2*F[1]+3*F[2]
    F[6]=3*F[1]+5*F[2]
    发现a,b是正常斐波那契数列的相邻两项。

    问题就变成了拓展欧几里得解决不定方程的问题。
    从第10项开始计算裴波那契数列直到能够求出方程的正整数解。
    如果当前的数列的项大于了n此题无解,不过题目似乎所有数据都有解。
    由于裴波那契数列是指数增加的,所以算法时间是 O(log^2(n)) 的。
    即使数据达到 10^18 也可以秒出答案,没有精度影响。

    代码参考:

    #include<cstdio>
    using namespace std;
    
    //暴力开 long long 
    typedef int int_;
    #define int long long
    
    //拓展欧几里得计算 a*x+b*y==gcd(a,b) 的某一个解 
    void exgcd(int a,int b,int &d,int &x,int &y){
        if(b==0){d=a;x=1;y=0;}
        else{exgcd(b,a%b,d,y,x);y-=x*(a/b);}
    }
    
    //解不定方程 a*x+b*y==c 求出对x的最小非负整数解 
    //无解返回 -1 
    int solve(int a,int b,int c){
        int d,x,y;
        exgcd(a,b,d,x,y);
        if(c%d)return -1;
        x*=(c/d);b/=d;
        return (x%b+b)%b;
    }
    
    int feb[1005];
    
    int_ main(){
        int N;scanf("%lld",&N);
        //递推求出到N的斐波那契数列 
        feb[0]=0;feb[1]=1;
        for(int k=2;;k++){
            feb[k]=feb[k-1]+feb[k-2];
            if(feb[k]>N)break;
        }
        //从第10项开始枚举求解 
        for(int k=10;feb[k]<=N;k++){
            int x=solve(feb[k-1],feb[k],N);
            if(x!=-1){
                int y=(N-feb[k-1]*x)/feb[k];
                if(y>=0){
                    int ans=feb[k]*x+feb[k+1]*y;
                    printf("%lld\n",ans);
                    break;
                }
            }
        }
        return 0;
    }
    
  • 1
    @ 2017-03-17 15:39:24

    官方题解
    作为我出的题目呢,数据保证了不超过30000,因此可以使用广义斐波拉契的发展趋势来做。
    为什么数据超过30000不能做了呢?因为计算机精度有限,否则是可以照样做下去的。
    几乎所有的广义斐波拉契数列(全为正整数)两项之商会无限趋于(根号5+1)/2,这也是黄金比例。
    因此可以用这个方法来做,当然黄金比例的值要取大一点。
    时间复杂度O(1)
    当然因为数据不大,大家也可以用楼上ZKX的方法做,更暴力,更准确。

    #include<algorithm>
    #include<iostream>
    #include<iomanip>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    using namespace std;
    inline const long long Get_Int() {
        long long num=0,bj=1;
        char x=getchar();
        while(x<'0'||x>'9') {
            if(x=='-')bj=-1;
            x=getchar();
        }
        while(x>='0'&&x<='9') {
            num=num*10+x-'0';
            x=getchar();
        }
        return num*bj;
    }
    const long double tmp=(1+sqrt(5))/2;
    long long n;
    int main() {
        n=Get_Int();
        printf("%lld\n",(long long)(tmp*n+0.5));
        return 0;
    }
    
  • 0
    @ 2017-03-17 17:54:02

    其实这道题啊!哼!这道题啊!不是很简单吗?
    观察题目我们不妨可以发现题目只有两重限制即:
    1.数据保证输入的数列中的数位于数列的第10项之后。
    2.数列中的数只可能是正整数。
    所以啊我们不妨搜一下,但注意到搜索范围过大后无法快速出答案,
    算了加剪枝把!
    Tips:

    #include<bits/stdc++.h>
    #include<ctime>
    using namespace std;
    int m,Rec;bool vst[30005],bj=1;
    inline int Get()
    {
    int num=0,bj=1;char x=getchar();
    while(!isdigit(x))
    {
    if(x=='-')bj=-1;
    x=getchar();
    }
    while(isdigit(x))
    {
    num=num*10+x-'0';

    x=getchar();
    }
    return num*bj;

    }
    inline bool OK(int R)
    {
    int ans=0,Last=R,now=m,cnt;
    while(true)
    {
    if(ans>=10&&!bj)return true;
    if(ans>10&&bj==1)return true;
    if(ans>=11&&bj==2)return true;

    cnt=now;if(cnt<=0)return false;
    now=Last-now;if(now<=0)return false;
    Last=cnt;if(Last<=0)return false;
    if(now>=Last)return false;
    ans++;

    }

    }
    int main()
    {
    srand((unsigned)time(NULL));bj=0;if(m>7500)bj=1;if(m>15000)bj=2;
    m=Get();Rec=rand()*17317%(2*m/5)+8*m/5;vst[Rec]=true;
    while(!OK(Rec))
    {
    Rec=rand()*17317%(2*m/5)+8*m/5;
    if(vst[Rec])continue;
    vst[Rec]=true;

    }

    printf("%d",Rec);
    return 0;
    }

  • 1

神探夏洛克之致命游戏2

信息

难度
6
分类
其他 | 数学数论 | Fibonacci数列欧几里得算法不定方程 点击显示
标签
递交数
22
已通过
5
通过率
23%
上传者