题目: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