Array
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
Vicky是个热爱数学的魔法师,拥有复制创造的能力。 一开始他拥有一个数列{1}。每过一天,他将他当天的数列复制一遍,放在数列尾,并在两个数列间用0隔开。Vicky想做些改变,于是他将当天新产生的所有数字(包括0)全加1。Vicky现在想考考你,经过100天后,这个数列的前M项和是多少?。
输入描述
输入有多组数据。 第一行包含一个整数T,表示数据组数。T. (1≤T≤2∗103) 每组数据第一行包含一个整数M. (1≤M≤1016)
输出描述
对于每组数据输出一行答案.
输入样例
3 1 3 5
输出样例
1 4 7
Hint
第一项永远为数字1,因此样例1输出1 第二天先复制一次,用0隔开,得到{1,0,1},再把产生的数字加1,得到{1,1,2},因此样例2输出前3项和1+1+2=4. 第三天先得到{1,1,2,0,1,1,2},然后得到{1,1,2,1,2,2,3},因此样例3输出前5项和1+1+2+1+2=7
已知前i项和sum(i),前2*i+1项和为sum(i)+sum(i) (复制的前i位)+1(0加1)+i(全部加1)
如果简单的有前i项求前2*i+1项直接可以求。
难点在求i到2*i+1之间的
其实也简单,如果n在i到2*i+1之间
第一部分 前i项和sum(i)
第二部分 i+1~n和,
n-i如果等于1 这部分就是1(0加1)
n-i大于1 1(0加1)+n-i-1(全部加1)+前n-i-1项和(递归求)
#include
#include
#include
#include
using namespace std;
long long f(long long n)
{
// printf("n=%I64d\n", n);
if(!n) return 0;
long long ans = 1;
long long i = 1;
while(2*i+1 <= n)
{
ans = ans*2 + (i+1);
i = 2*i+1;
}
long long tmp = n-i-1;
if(tmp >= 0) ans += f(tmp)+1+tmp;
return ans;
}
int main(void)
{
int T;
scanf("%d", &T);
while(T--)
{
long long n;
int i;
scanf("%I64d", &n);
printf("%I64d\n", f(n));
}
return 0;
}