51Nod1149 Pi的递推式

题目

F ( x ) = 1 , x [ 0 , 4 )
F ( x ) = F ( x 1 ) + F ( x π ) , x [ 4 , + )
F ( n ) n 10 6

题解

假设问题是问每次只加1或2,加到 n 的方案数。
F ( n ) = F ( n 1 ) + F ( n 2 )
斐波那契?
没错。
O ( n ) 的另一种方法求,就是求 Σ a + 2 b = n C a + b a
倒着推不好推?那就顺着推。
那么这道题目就将2替换为 π ,加到的目标数在区间 ( n 4 , n ]
假设现在已经加到了 ( n 4 π , n 4 ] ,则加 π 就可以达到目标。
假设现在已经加到了 ( n 4 1 , n 4 ] ,则加 1 就可以达到目标。
难点难在加到的目标数属于一个区间。
对于第一种情况,枚举加了多少个 π ,则加的 1 的个数也确定了。你会发现除法有余数。这个余数的范围就对应着区间 ( n 4 1 , n 4 ]
对于第二种情况,枚举加了多少个 1 ,则加的 π 的个数也确定了。你会发现除法有余数。这个余数的范围就对应着区间 ( n 4 π , n 4 ]
所以就可以愉快地做题了。

心得

从大神们那里得知,问题 F ( n ) = F ( n a ) + F ( n b ) 都可以用时间复杂度为 O ( n ) 的算法求出答案。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000010
#define DB double 
#define LL long long
#define mo 1000000007
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const DB pi=acos(-1);
LL i,j,n,m,ans;
LL jc[N],ny[N];
LL ksm(LL x,LL y){
    LL rs=1;
    for(;y;y>>=1,x=(x*x)%mo)if(y&1)rs=(rs*x)%mo;
    return rs;
}
void pre(){
    LL i;
    jc[0]=jc[1]=ny[0]=ny[1]=1;
    fo(i,2,N-10)jc[i]=(jc[i-1]*i)%mo;
    ny[N-10]=ksm(jc[N-10],mo-2);
    fd(i,N-11,2)ny[i]=(ny[i+1]*(i+1))%mo;
}
LL C(LL n,LL m){return ((jc[n]*ny[m])%mo*ny[n-m])%mo;}
void add(LL &x,LL y){x=x+y>=mo?x+y-mo:x+y;}
int main(){
    scanf("%lld",&n);
    if(n<4){printf("1");return 0;}
    pre();
    fo(i,0,n-4){
        j=(DB)(n-4-i)/pi;
        add(ans,C(i+j,i));
    }
    m=(n-4)/pi;
    fo(i,0,m){
        j=(DB)(n-4-i*pi);
        add(ans,C(i+j,i));
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huangjingyuan107/article/details/80426709