51nod 算法马拉松6(索函数)(规律题目)

本文介绍了一种利用斐波那契数列求解特定问题的方法,通过对数转换简化计算过程,并提供了详细的数学归纳法证明及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

总的来说是要先找规律的题目,然后就是一个球log2(f[ n ])的技巧。

这题目的解题报告说的很详细:

通过列出前几项观察可以发现,答案其实是2^k-1,其中k为fib[n]在二进制表示中的位数,记为bit(f[n])。

下面来证明该结论。用数学归纳法。

对于m=0,1时该结论显然成立。设当m<n时成立。

当m=n时,

由于f[n]=f[n-1]+f[n-2]那么这个时候bit[f[n]]要么等于bit(f[n-1])要么等于bit(f[n-1])+1。

当bit(f[n])==bit(f[n-1])时,Sor(n)=Sor(n-1)=2^(bit(f[n-1]))-1,结论成立。

当bit(f[n])==bit(f[n-1])+1时,Sor(n)=Sor(n-1)*2+1=2^(bit(f[n-1])+1)-1=2^(bit(f[n]))-1,结论成立。

所以原结论成立。

由于n比较大,直接计算位数出不来。可以通过取对数来解决。

由斐波那切的通项公式F[n]=(((1+sqrt(5))/2)^n-((1-sqrt(5))/2)^n)/sqrt(5)= (((1+sqrt(5))/2)^n/sqrt(5))*(1-((1-sqrt(5))/(1+sqrt5))^n)

F[n]在二进制下的位数是(int)log2((((1+sqrt(5))/2)^n/sqrt(5))*(1-((1-sqrt(5))/(1+sqrt5))^n))+1

当n比较的时候直接暴力计算出位数即可。

当n比较大的时候由于(1-((1-sqrt(5))/(1+sqrt5))^n))=1

那么计算(int)log2((((1+sqrt(5))/2)^n/sqrt(5))+1这个即可。

把n提前面来就可以O(1)计算,(int)(n*log2((1+sqrt(5)) /2)- log2(sqrt(5)))+1

最后用快速幂取余计算出结果。

#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define rep1(i,x,y) for(int i=x;i<=y;i++)

typedef long long ll;
typedef unsigned long long llu;
const ll base = (ll)1e17;
const int N = 1e5 + 100;
const int mod = 1e9 + 7;
const ll tbase = base%mod;

int bit_(llu n){ return (int)log2(n) + 1 ;}
llu f[N],d[N],lim = 83 , yu[N];
void init(){
   f[0] = 0;f[1] = 1; yu[0] = 1;
   d[0] = 0; d[1] = 1;
   for(int i=1;i<=100;i++) yu[i] = yu[i-1]*2%mod;
   for(int i=2;;i++){
       f[i] =f[i-1]+f[i-2];
       d[i] = (yu[bit_(f[i])]-1+mod)%mod;
       if(f[i] > base){
           break;
       }
   }
}
ll n;
ll pow_(ll a, ll b){
   ll ans = 1 , te = a;
   while(b){
       if(b&1) ans = ans*te%mod;
       b>>=1;
       te=te*te%mod;
   }
   return ans;
}
inline ll read(ll& n){
   n = 0; int ch = getchar();
   while(!isdigit(ch)) ch=getchar();
   while(isdigit(ch)) n = n*10 + ch-'0' , ch=getchar();
}
int main()
{
   init();
   int T;
   scanf("%d",&T);
   while(T--){
      read(n);
      if(n < 80)
        printf("%d\n",(int)d[n]);
      else {
         long long res = (ll)((double)n*log2((1+sqrt(5))/2)- log2(sqrt(5)))+1;
         printf("%d\n",(int)((pow_(2,res)-1+mod)%mod));
      }
   }
   return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值