**题目:**http://acm.hdu.edu.cn/showproblem.php?pid=6304
题意:
求前n项和
分析:
先打个表,每个数出现次数如下:
发现,i会出现(累加)__builtin_ctz(i)+1次
那么,二分出现最大的数x
再求和即可
注意:有的地方是整除2,不能直接乘2的逆元;有的地方不用逆元处理会爆longlong
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7;
const ll inf=1e18;
const ll rev=500000004;
ll n,ans;
ll check(ll x)
{
ll sum=2+((x-1)>>1),num=1; //奇数的个数
for(ll tmp=2;tmp<=x;tmp<<=1)
{
num++;
sum+=((x/tmp+1)>>1)*num;
}
return sum;
}
void work(ll x)
{
if((x&1)==1) ans=(2+(x>>1)%MOD*((3+x)%MOD)%MOD*rev%MOD)%MOD;
else ans=(2+((x-1)>>1)%MOD*(((2+x)>>1)%MOD))%MOD;
ll t,tmpmod,num=1,tAdd1,tto1;
for(ll tmp=2;tmp<=x;tmp<<=1)
{
t=x/tmp;
tmpmod=tmp%MOD;
num++;
tAdd1=((t+1)>>1)%MOD;
tto1=(t>>1)%MOD;
if(t%2==1) ans=(ans+tAdd1*tAdd1%MOD*tmpmod%MOD*num%MOD)%MOD;
else ans=(ans+tto1*tto1%MOD*tmpmod%MOD*num%MOD)%MOD;
}
return;
}
int main()
{
int T;
ll l,r,mid;
scanf("%d",&T);
while(T--)
{
scanf("%I64d",&n);
if(n==1) printf("1\n");
else if(n==2) printf("2\n");
else if(n==3) printf("4\n");
else
{
l=max(2ll,n/2-100);r=n/2+100;
while(r>l)
{
mid=(l+r+1)>>1;
if(check(mid)<=n) l=mid;
else r=mid-1;
}
work(l);
//+(l+1);
n-=check(l);
n%=MOD;
ans=(ans+(l+1)%MOD*n%MOD)%MOD;
printf("%I64d\n",ans);
}
}
return 0;
}
打表代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int tmax=200;
int a[tmax],cnt[tmax];
int main()
{
int i,j;
a[1]=a[2]=1;
printf("%3d:%4d\n%3d:%4d\n",1,a[1],2,a[2]);
for(i=3;i<=200;i++)
{
a[i]=a[i-a[i-1]]+a[i-1-a[i-2]];
printf("%3d:%4d\n",i,a[i]);
cnt[a[i]]++;
}
cnt[1]=2;
printf("!!!!!\n");
for(i=1;i<=200;i++)
{
printf("%d:\n",i);
for(j=1;j<=200;j++)
if(cnt[j]==i) printf("%d ",j);
cout<<endl;
}
return 0;
}
这题,交了两页才过,纪念一下。