您的位置:首页 > 其它

HDU 5587 数学

2015-11-29 16:50 288 查看
Array Accepts: 112 Submissions: 324

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. \left( 1 \leq T \leq 2 * {10}^{3} \right)(1≤T≤2∗10

​3

​​ )

每组数据第一行包含一个整数M. \left( 1\leq M \leq {10}^{16} \right)(1≤M≤10

​16

​​ )

输出描述

对于每组数据输出一行答案.

输入样例

3

1

3

5

输出样例

1

4

7

题解:

我们可以发现,可以把这个数列按一个满二叉树的形式组织起来,a1 = 1,a2 = 1,a3 = 2…这颗树任何一个节点的左儿子等于父亲节点,而右儿子等于父亲节点加1.

1,我们令An为这棵树每一层的节点和。由于题目的复制关系可知,A[n+1] = 2*A
+2^(n-1).

2, 我们用Sn表示An的前n项和,由An的递推公式可知S[n+1]-A[1] = 2*S
+ 2^n - 1.即为S[n+1]= 2*S
+ 2^n
。由此公式即可把每一个满子数的前n项和求出来。例如S[3]就表示了前三层所有节点的和。

3,假如我们要求n = 28(图中黑笔表示的数字)用sum[28]表示我们要求的结果。那么可以知道这个结果由两部分组成,一个是S[3](因为28 > 2^4-1),另一个是在第四层的剩余数列和1,2,2,3,2,3,3,4,2,3,3,4,3。

4,求第四层的数列和时,由题目的构造规则可知。当第四层每一项都减1时变为0,1,1,2,1,2,2,3,1,2,2,3,2,总共13个数,就变为了求sum[12].

5,然后求sum[12]

重复上述步骤。当n == 0或者n是满树的最右下方结点时返回。



#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <sstream>
#define f(i,a,b) for(int i = a;i<=b;i++)
#define fi(i,a,b) for(int i = a;i<=b;i--)

using namespace std;

#define LEN 20
#define MOD 10000

long long G[60],a2[60];

long long dfs(long long k){
if(k == 0) return 0;
long long i,j;
for(i = 1;;i++)if(a2[i]>=k) break;
if(a2[i] == k) return G[i];
long long cnt = i-1;
long long sum = G[cnt] + k - a2[cnt];
return sum + dfs(k - a2[cnt] - 1);
}

int main()
{
//    freopen("data.in","r",stdin);
//    freopen("data.out","w",stdout);
int N;
long long kk = 2;
G[1] = 1;
a2[1] = 1;
f(i,1,58){
G[i+1] = 2*G[i] + kk;
kk*=2;
a2[i+1] = kk - 1;
}
scanf("%d",&N);
while(N--){
long long n;
scanf("%lld",&n);
printf("%lld\n",dfs(n));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数学 hdu