题目:http://acm.hdu.edu.cn/showproblem.php?pid=5358
题意:
分析:首先要知道[log2(x)]+1代表x的位数,而且根据题意不会超过35,那么枚举位数i:1~35。对于每一位i找到区间[x,y],使得S(x,y)的二进制表示的位数等于i,此时的贡献为i*(x+y)。那么对于每一个i,怎么找出所有符合条件的区间[x,y]?1~n枚举起点x,那么y会在一段范围[l,r]内满足条件。下次x变成x+1,即起点x向右移位,那么现在要找的y的区间为[l‘,r‘],l‘不至于小于l同样r‘不至于小于r。这样可以用O(n)的复杂度找出所有区间使得区间和的二进制表示的位数为枚举的i。总的时间复杂度为35*N。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 1e5+6;
LL pow2l[50],pow2r[50],s[maxn];
int main()
{
for(int i=1;i<=40;i++)
{
pow2l[i]=(1ll<<i);
pow2r[i]=((1ll<<(i+1))-1);
}
pow2l[0]=0;
pow2r[0]=1;
int ncase,i,j,n,x;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=s[i-1]+x;
}
LL ans=0;
for(i=1;i<=35;i++)
{
if(s[n]<pow2l[i-1])
break;
LL l=1,r=0,temp=0;
for(j=1;j<=n;j++)
{
l=(l>j?l:j);
while(l<=n && s[l]-s[j-1]<pow2l[i-1])
l++;
r=(r>l-1?r:l-1);
while(r+1<=n && s[r+1]-s[j-1]>=pow2l[i-1] && s[r+1]-s[j-1]<=pow2r[i-1])
r++;
if(r>=l)
temp+=(r-l+1)*j+(r+l)*(r-l+1)/2;
}
ans+=temp*i;
}
printf("%I64d\n",ans);
}
return 0;
}原文:http://blog.csdn.net/w20810/article/details/47346349