标签:
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 417 Accepted Submission(s): 211
题目大意:
一开始vicky拥有一个数列{1}。每过一天,他将他当天的数列复制一遍,放在数列尾,并在两个数列间用0隔开。Vicky想做些改变,于是他将当天新产生的所有数字(包括0)全加1。Vicky现在想考考你,经过100天后,这个数列的前M项和是多少?。
解题思路:首先我们可以先写出某天的序列{1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}。我们可以首先将第i天的序列和以及序列长度递推出来,如果给的M正好是一个某天后的序列长度
,那么我们很容易可以得到序列和。但是如果M不是某天后的序列长度,我们需要将最长的序列和先拿出来求这段子序列的和。然后再处理剩下的序列,同时应该把这时候剩下的序列的第一个元素即1减掉,减掉后剩下的序列是他前边求出的序列元素+1后得到的,这时候,我们应该继续求出剩下序列的最长序列,然后拿出来求和,但是这时候求的序列元素应该都+1,然后再减去第二次拿掉序列后的第一个元素即2减掉,重复上述操作。 (不好描述,看代码)
#include<stdio.h> using namespace std; typedef long long LL; LL num[100],sum[100]; void prin(){ //预处理出来第i天的序列长度,第i天的序列和 num[1] = 1; for(int i = 2; i <= 63; i++){ num[i] = num[i-1]*2 + 1; } sum[1] = 1; for(int i = 2; i <= 63; i++){ sum[i] = sum[i-1]*2+num[i-1]+1; } } int main(){ int T , p; LL n; prin(); scanf("%d",&T); while(T--){ scanf("%lld",&n); LL ans = 0,coun = 0; while(n){ p = 1; while(num[p] <= n){ //找到最长的序列 p++; } p--; ans += sum[p] + num[p]*coun; //截取出来的序列的序列和 n -= num[p]; //求出剩下序列长度 if(n != 0){ coun++; //记录经过多少次复制得到 ans += coun; //减掉序列开头那个元素 n--; //序列长度减一 } } printf("%lld\n",ans); } return 0; }
标签:
原文地址:http://www.cnblogs.com/chengsheng/p/5010728.html