【HDU5587 BestCoder Round 64 (div1)B】【迭代 前缀和思想】Array 前m个数的二进制中共有多少个1

来源:互联网 发布:手机vpn软件 编辑:程序博客网 时间:2024/05/19 13:58
#include<stdio.h>#include<string.h>#include<ctype.h>#include<math.h>#include<iostream>#include<string>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}#define MS(x,y) memset(x,y,sizeof(x))#define MC(x,y) memcpy(x,y,sizeof(x))#define MP(x,y) make_pair(x,y)#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}const int N=0,M=0,Z=1e9+7,ms63=1061109567;int casenum,casei;LL day[64];LL sumd[64];LL sumv[64];void init(){for(int i=1;i<=60;++i){day[i]=1ll<<i-1;sumd[i]=(1ll<<i)-1;sumv[i]=sumv[i-1]*2+day[i];}}LL cnt(LL d,int p){while(d<sumd[p])--p;//使得d>=sumv[p]LL more=d-sumd[p];LL tmp=sumv[p]+more;if(more>1)tmp+=cnt(more-1,p-1);return tmp;}int main(){init();scanf("%d",&casenum);for(casei=1;casei<=casenum;++casei){LL m;scanf("%lld",&m);printf("%lld\n",cnt(m,60));}return 0;}/*【trick&&吐槽】读题非常非常重要。这题卡了很久就是卡在题意上QwQ【题意】给你一个奇怪的数列,一开始只有{1},后来每次先添加一个0,然后把之前的数列再重复写一遍,新增的每个数再加1。问你这个数列的前m(1e16)项的和是多少。【类型】迭代 前缀和思想【分析】我们很难确定每个位置的是多少。然而事实上也不需要知道这个,我们只要知道前多少位数值的和即可。这个可以以二进制为基准倍增,权值每次*2再加上区间长度。我们处理完这个之后,每次就找前缀数值。而后面剩余的数,只是前面倍增,每个数值+1而已。于是这个时候,我们把多1的数值算过之后,把所有数映射到前面来。这样对于每个询问,我们可以在log级别求解。这样就做完啦!=========================================================然而,这道题还有值得思考的地方。就是这个数列的意义。a[i]其实表示的是i的二进制中有多少个1,你看——a[]={1,1,2,1,2,2,3,...}哇塞~~好有趣哦~~然而并不会使得我们的做题策略改变噗【时间复杂度&&优化】O(Tlogm)*/

0 0
原创粉丝点击