题目:求数列的前n项和。
每个数出现的次数是log(lowbit(i))+1次,可以二分出a[n]的值,对于同样的次数会构成了一个等差数列。公差就是2^i,项数是n/2^i.
太菜了自己打表没弄出来,参考了https://blog.csdn.net/purple_bro/article/details/81177315写的好详细。
#include<bits/stdc++.h>
using namespace std;
void dabiao()
{
int a[100],sum[100];
vector<int>num[100];
a[1]=a[2]=1;
sum[1]=1;sum[2]=2;
for(int i=3;i<100;i++)
{
a[i]=a[i-a[i-1]]+a[i-1-a[i-2]];
sum[i]=sum[i-1]+a[i];
printf("%2d:%4d%6d\n",i,a[i],sum[i]);
}
for(int i=1;i<100;i++)
{
int tmp=1,k=a[i];
while(a[i]%2==0)
{
a[i]/=2;
tmp++;
}
num[tmp].push_back(k);
}
for(int i=1;i<=8;i++)
{
for(int j=0;j<num[i].size();j++)
printf("%4d",num[i][j]);
puts("");
}
}
typedef long long ll;
const ll mod=1e9+7;
ll p[65],n;
ll qmod(ll x,ll p)
{
ll ans=1;
while(p)
{
if(p&1)
ans=ans*x%mod;
x=x*x%mod;
p>>=1;
}
return ans;
}
bool check(ll x)//除以每个2^i就可以得出2^i有多少个,这样子加完就得到对应的a[x]是多少。
{
ll num=0;
for(int i=0;i<=63;i++)
num+=x/p[i];
return num>=n-1;
}
ll solve(ll x)
{
ll num=0;
for(int i=0;i<=63;i++)
num+=x/p[i];
return num;
}
int t;
int main()
{
dabiao();
ll inv2=qmod(2ll,mod-2);
p[0]=1;
for(int i=1;i<=63;i++)
p[i]=p[i-1]*2;
scanf("%d",&t);
while(t--)
{
scanf("%lld",&n);
ll l=max(n/2-30,1ll),r=min(n,n/2+30),pos;
while(l<=r)//二分找n对应的a[n],pos记录下a[n]
{
ll mid=(l+r)>>1;
if(check(mid))
{
pos=mid;
r=mid-1;
}
else l=mid+1;
}
ll s=pos-1,ans=1;//pos-1是因为在a[n]位置的n可能不是刚好符合2^i,所以要找前面一个,前面一个必定是符合2^i个的
for(int i=0;i<=63;i++)
if(s>=p[i])
{
ll a1=1,an=s/p[i]%mod;
(ans+=p[i]*(a1+an)%mod*an%mod*inv2%mod)%=mod;//等差数列求和
}
(ans+=(n-1-solve(s))%mod*pos%mod)%=mod;///处理最后多出来的那些不参与上面等差数列的项
printf("%lld\n",ans);
}
return 0;
}