Chiaki Sequence Revisited
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2106 Accepted Submission(s): 585
Problem Description
Chiaki is interested in an infinite sequence a1,a2,a3,..., which is defined as follows:
an={1an−an−1+an−1−an−2n=1,2n≥3
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
题意:
给你一个序列,序列的公式在上面,然后输入一个n,让你输出a[1..n]的和,即他的前缀和
解析:
这种题一开始都是打表找规律,队友找到的规律是a[i]的值是按规律递增的,并且每一个值出现的次数都是他分解成k=2^w*p(w是该数最大的能被2整除的最大幂数)后,w+1次。譬如说12=2^2*4,所以12这个数会出现3次并且是连续出现的,即有3项a[i]=12(i=l,l+1,l+2),l未知。
把他们列出来就可以找到规律了。
1 3 5 7 9 ....... 出现1次的差为2的等差数列
2 6 10 14 18..... 出现2次的差为4的等差数列
4 12 20 28 36 ..... 出现3次的差为8的等差数列
8 24 40 56 72 .... 出现4次的差为16的等差数列
........
这样我们只要找到出现1次的等差数列的长度、出现2次的等差数列的长度、出现3次的等差数列的长度、......
我们就可以得到前缀和了。
那么我们首先就该知道a[n]这个数的值,这样才能求出上面各个等差数列的长度。
所以我们就二分它,因为你再观察一下这个数列就可以发现,这个每一个a[i]的值一定是在[i/2,i]之间的。
我们就二分它,那么如何验证。
首先知道一个数的值12=4*3,我们其实就可以得到各个等差数列的长度了。
1*12 ....(1,3,5,7,9,11)出现1次的等差数列长度为6
2*6 ....(2*1,2*3,2*5) 出现2次的等差数列长度为3
4*3....(4*1,4*3) 出现3次的等差数列长度为2
8*1.5 ....(8*1) 出现4次的等差数列长度为1
这样各个等差数列的长度就得到了。
那么验证的时候我们只要通过我们每次二分出来的值,来算出上述的长度。
并且这里需要注意因为n的范围是1e18,所以当最后得到a[n]的值计算前缀和的时候,作乘法的两个乘数都是需要模的,不然会爆long long !!!
ans=(tmp%MOD*(n%MOD))%MOD,这样子。
#include <cstdio>
#include <cstring>
typedef long long ll;
const int MOD = 1e9+7;
inline void solve(ll x,ll &a,ll &b)
{
ll y=x;
ll res=y/2+y%2;
ll cnt=1;
while(y%2==0)
{
cnt++;
y=y>>1;
res+=(y/2+y%2)*cnt;
}
ll tmp=cnt;
while(y)
{
cnt++;
y=y>>1;
res+=(y/2+y%2)*cnt;
}
a=res-tmp+2;
b=res+1;
}
inline ll cal(ll y,ll x)
{
ll pre=y;
ll res=y/2+y%2;
ll ans=(res%MOD+(res%MOD*((res-1)%MOD))%MOD+1)%MOD;
ll cnt=1;
ll t2=1;
ll pp,tmp;
while(y%2==0)
{
t2=t2*2%MOD;
cnt++;
y=y>>1;
if(y%2==0)
{
pp=(y/2+y%2);
tmp=(((pp%MOD)*(t2%MOD))%MOD+((pp%MOD*((pp-1)%MOD))%MOD*(t2%MOD))%MOD)%MOD;
tmp=((tmp%MOD)*(cnt%MOD))%MOD;
}
else
{
pp=(y/2+y%2)-1;
tmp=((pp%MOD*(t2%MOD))%MOD+((pp%MOD*((pp-1)%MOD))%MOD*(t2%MOD))%MOD)%MOD;
tmp=(tmp%MOD*(cnt%MOD))%MOD;
tmp=(tmp+(pre%MOD*(x%MOD))%MOD)%MOD;
}
ans=(ans+tmp)%MOD;
}
while(y)
{
t2=t2*2%MOD;
cnt++;
y=y>>1;
pp=(y/2+y%2);
tmp=((pp%MOD*(t2%MOD))%MOD+((pp%MOD*((pp-1)%MOD))%MOD*(t2%MOD))%MOD)%MOD;
tmp=((tmp%MOD)*(cnt%MOD))%MOD;
ans=(ans+tmp)%MOD;
}
return ans;
}
ll bin_search(ll x)
{
ll l,r;
l=x/2;r=x;
while(l<r)
{
ll mid=(l+r)>>1;
ll a,b;
solve(mid,a,b);
if(b<x)
{
l=mid+1;
}
else if(a>x)
{
r=mid;
}
else
{
return cal(mid,x-a+1);
break;
}
}
/*ll a,b;
solve(l,a,b);
return cal(l,x-a+1);*/
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ll x;
scanf("%lld",&x);
if(x==1) printf("1\n");
else if(x==2) printf("2\n");
else
{
printf("%lld\n",bin_search(x));
}
}
return 0;
}