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