题目链接:http://acm.hdu.edu.cn/listproblem.php?vol=54
没看懂的大佬的代码orz:
题意就是给了个数列求和
打表会发现在无视掉第一个1之后数列是单调递增的且只增1,其中数字i出现的次数lowbit(i)就是数字里2的因子数目+1
(4 就是2*2=4,2+1个, 6就是2*3,1+1个,8就是2^3,3+1=4个)
所以得到N 后先二分找到一个pos 使得 lowbit(1)+lowbit(2)+..+lowbit(pos)>=n 且pos 要最小
之后先把i=1~pos-1 的加起来(按照能被2^k整除分类发现是等差数列),加上i=pos的(剩下的项数*pos)和被抛开没看的a1=1就是答案了
另外有一点不太好理解的是求项数。。。。。
比如 8=2^3, 那么8肯定能是 2^0, 2^1 ,2^2 ,2^3次方的倍数
数列按照被整除分组那么就是
1,2,3,4,5,6,7,8……………………第一组2^0
2,4,6,8,10,12,14……………………第二组2^1
4,8,12,16,20,24,28…………………第三组2^2
8,16,24,32,40…………………………第四组2^3
以及等等
每个数列中项数就为 n/2^k
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=(ll)1e9+7;
ll p[65],n,ni2,ans;
int t;
ll poow(ll x,ll b)
{
ll ans=1;
while(b)
{ if (b&1)ans=ans*x%mod;
x=x*x%mod;
b>>=1;
}
return ans;
}
bool check(ll x)
{ ll cnt=0;
for (int i=0;i<=62;i++)
{cnt+=x/p[i];
if (cnt>=n-1) return true;
}
return cnt>=n-1;
}
//求到x的项数
ll summ(ll x)
{ ll cnt=0;
if (x==0)return 0;
for (int i=0;i<=62;i++)cnt+=x/p[i];
return cnt;
}
int main(){
ni2=poow(2,mod-2);//数列求和的时候会用到2的逆元
p[0]=1;
for (int i=1;i<=62;i++)p[i]=p[i-1]*2;
scanf("%d",&t);
while(t--){
scanf("%lld",&n);
if (n==1){printf("1\n");continue;}
else{
//二分
ll l=n/2-30,r=n/2+30,mid,pos;
if (l<0)l=1ll;if (r>n)r=n;
while(l<=r){
mid=(l+r)>>1;
if (check(mid)){pos=mid;r=mid-1;}else l=mid+1;
}
ll res=pos-1;
ans=0;
for (int i=0;i<=62;i++)
{
if (p[i]>res)break;
ll temp= res/p[i];//项数,首项为 p[i],尾项为temp*p[i]
temp%=mod;
ans= (ans+p[i]%mod*(temp+1)%mod*temp%mod*ni2%mod)%mod;
}
ans= (ans+(n-1-summ(res)%mod)%mod*pos%mod)%mod;
printf("%lld\n",(ans+1)%mod);
}
}
return 0;
}