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;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章