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

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

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

通过列出前几项观察可以发现,答案其实是2^k-1,其中kfib[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;
}



你可能感兴趣的:(51nod 算法马拉松6(索函数)(规律题目))