Chiaki Sequence Revisited(二分)


 

Problem Description

Chiaki is interested in an infinite sequence a1,a2,a3,... , which is defined as follows:

An = A(n-A(n-1))+A(n-1-A(n-2));


Chiaki would like to know the sum of the first n terms of the sequence, i.e. ∑i=1nai . As this number may be very large, Chiaki is only interested in its remainder modulo (109+7 ).

 

Input

There are multiple test cases. The first line of input contains an integer T (1≤T≤105 ), indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤1018 ).

 

 

Output

For each test case, output an integer denoting the answer.

 

 

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

 

思路: 首先打表后发现An数组中元素出现次数有规律可循。
       即: 数 Ai 出现的次数为 Ai二进制后面0的个数加1。
   
    题目中给定n,让求的是前n项和。假设知道An,那么
    我们可以这样来求前n项和:
    示例 : 
    1.2.3... An 首先出现了一次
    2.4.6.8...Ai(Ai < An && Ai%2 == 0)  第二次出现。
    4.8.12...Aj (Aj < An && Ai%4 == 0)  第三次出现。
    ...
    最多也就枚举到 (1<<63)。而且每一行都是等差数列,求和没问题了。
    这和求n!后面0的个数的算法思想是一致的。
   
   
    那么问题是, An 怎么求 ?
    考虑这两个问题 :
    1. 给了n怎么求 An ?
    2. 给了An怎么求 n ?
    1问题明显就是我们想要的。
    2问题则是可求的。
    为什么呢 ? 由上面的示例可以知道给了An小与等于An的数出现的次数
    都是可以求出来的。即:
    An 项第一次出现
    An/2 项重复出现一次
    An/2/2重复出现二次。
    。。。
    那么小于等于An的数出现的次数和代表什么意思呢?
    不就是n嘛?
    (第一个1没考虑在内,特殊。。)
    更确切一点说,应该是满足等于An的最后一个n,因为可能多个数等于An的。
    说到这里二分一下就能求出An的值了。
   
    还有一点不得不说,因为给定的n可能是连续的几个相同数字中中间的一个,因此
    算结果的时候需要特别算一下。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e6+7;
ll mod2;
ll A[maxn];
ll check(ll An)
{
    ll n = 0;
	while(An)
	{
		n += An;
		An /= 2;
	}	
    return n+1;
}
ll qm(ll a, ll n)
{
    ll ans = 1;
	while(n)
	{
		if(n%2) ans=ans*a%mod;
		a = a*a % mod;
		n /= 2;
	}	
	return ans;
}
int main()
{
	int t;
	mod2 = qm(2,mod-2);
	scanf("%d",&t);
	while(t--)
	{
	    ll n;
		scanf("%lld",&n);
		if(n == 1) 
		{
		    printf("1\n");
		    continue;
		}
		if(n == 2)
		{
			printf("2\n");
			continue;
		}
		ll An = 0;
		ll l = n/2-200,r = n/2+200;
		while(l <= r)
		{
			ll mid = (l+r) / 2;
			if(check(mid) >= n)
			{
				An = mid;
				r = mid-1;
			}
			else l = mid+1;
		}
		ll ans;
		if(check(An) == n) ans = 0;
		else{
		    ans = ((n-check(An-1))%mod)*(An%mod)%mod;
		    An--;
		}	
		for(ll i = 1; i <= An; i<<=1)
		{
			ll tp = An / i;
			ans += (i%mod*(tp%mod)+ (tp-1+mod)%mod*(tp%mod)%mod*(i%mod)%mod*mod2)%mod;//n*a1 + (n-1)*n/2 
			ans %= mod;
		}		
		printf("%lld\n",(ans+1)%mod);
	}
	return 0;
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值