总的来说是要先找规律的题目,然后就是一个球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;
}