/**
G: Chiaki Sequence Revisited
链接:http://acm.hdu.edu.cn/showproblem.php?pid=6304
题意:求解所给序列的前缀和;
分析:先拍出40个a[i]的表 得到:
1 1 2 2 3 4 4 4 5 6 6 7 8 8 8 8 9 10 10 11 12 12 12 13 14 14 15 16 16 16 16 16 17 18 18 19 20 20 20 21
对于任意两个相邻且不等的数字,差值均为1
对于每个数字第一次出现的位置打上标记,可以发现是一个2^k的循环
对于每一个数字n,首先可以找到小于等于它的最大的2^k -1个数字
而2^k-1个数字其对应的a的值只增加了2^(k-1)个数字
最后你就会发现:
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的等差数列
因此只需要知道等差数列的长度 就可以获得前缀和了;
然而接下来 窝并不知道二分 难道是签到题吗???好烦啊.....
为什么我观察不出来呢....................... 挂比我的好吧
好了 那就直接说吧 二分 二分找出a[n] 为了求出前n项和 你会发现次数为i的数的首相为2^(i-1),公差为2^i。
那么通过这个规律 只需要枚举出现的次数 然后计算前缀和即可得到答案;
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double eps=1e-6;
const int maxn=1e3+7;
const int mod=1e9+7;
ll a[maxn],f[maxn];
int main(){
ll T;
scanf("%lld",&T);
a[0]=f[0]=1;
for(int i=1;i<=62;i++) a[i]=a[i-1]*2+1,f[i]=f[i-1]*2;
ll inv=(mod+1)/2;
while(T--){
ll pos=0,n,m,ma=-1,ans=0;
scanf("%lld",&n);
m=n;
for(ll i=62;i>=0;i--) if(m-a[i]>=0) m-=a[i],pos=pos+(1ll<<i);
pos+=(m>0);
ll now=pos-1,num=0;
for(ll i=1;f[i-1]<=now;i++){
ll s=f[i-1],step=(now-s)/f[i]+1,e=(step-1)*f[i]+s;
ll haha=(s+e)%mod*(step%mod)%mod*inv%mod;
ans=(ans+haha*i%mod)%mod;
num+=step*i;
}
ans=(ans+1+(n-num-1)%mod*(pos%mod)%mod)%mod;
printf("%lld\n",ans);
}
}
HDU 6304 Chiaki Sequence Revisited 二分+规律
猜你喜欢
转载自blog.csdn.net/hypHuangYanPing/article/details/81783237
今日推荐
周排行