Description
序列anan满足a1=a2=1,an=an−an−1+an−1−an−2,n≥3a1=a2=1,an=an−an−1+an−1−an−2,n≥3,给出一整数nn,求
Input
第一行一整数TT表示用例组数,每组用例输入一整数
Output
输出∑i=1nai∑i=1nai
Sample Input
10
1
2
3
4
5
6
7
8
9
10
Sample Output
1
2
4
6
9
13
17
21
26
32
Solution
可以发现aa序列有两个规律,首先的值较an−1an−1每次增加00或,其次,假设an=2m⋅k,kan=2m⋅k,k为奇数,那么anan在序列中出现m+1m+1次
以此规律,令num[x]num[x]表示前xx个自然数在该序列中出现的次数,表示在该序列中出现的前xx个自然数的和
对于~xx中的奇数,在序列中只出现一次,对于~xx中的偶数,把每个数字除以二则转化为求和sum[⌊x2⌋]sum[⌊x2⌋],由于2k2k出现次数比kk多一次,故有转移:
二分找到最大的xx满足,答案即为sum[x]+(n−num[x])⋅(x+1)sum[x]+(n−num[x])⋅(x+1),时间复杂度O(Tlog2n)O(Tlog2n)
Code
#include<cstdio>
using namespace std;
typedef long long ll;
#define mod 1000000007
#define inv2 500000004
ll count(ll n)
{
if(n==1)return 2;
return n+count(n/2);
}
ll Solve(ll n)
{
if(n==1)return 1;
return (n%mod*((n+1)%mod)%mod*inv2%mod+2ll*Solve(n/2)%mod)%mod;
}
int main()
{
int T;
ll n;
scanf("%d",&T);
while(T--)
{
scanf("%I64d",&n);
if(n<=2)printf("%I64d\n",n);
else
{
ll l=1,r=1e18,pos,mid;
while(l<=r)
{
mid=(l+r)/2;
if(count(mid)<=n)pos=mid,l=mid+1;
else r=mid-1;
}
ll ans=(Solve(pos)+1)%mod;
ans=(ans+(n-count(pos))*(pos+1)%mod)%mod;
printf("%I64d\n",ans);
}
}
return 0;
}