2018 杭电多校第一场 Chiaki Sequence Revisited

题目链接:http://acm.hdu.edu.cn/listproblem.php?vol=54

没看懂的大佬的代码orz:

https://github.com/2014CAIS01/The-road-to-ACMer/blob/master/Misc/2018_Multi_University/HDU/Day01/G.cpp#L1

题意就是给了个数列求和

打表会发现在无视掉第一个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;
}

猜你喜欢

转载自blog.csdn.net/hjsss3/article/details/81185719