题意:给你n个数,求他给出的求和式
题解:由于求和式中含有log2,当我们枚举左端点的时候,右端点在一定范围内log2值是不会变的,所以我们可以对每一种log2的取值做枚举,然后枚举每一个左端点,再找到右端点符合条件的范围,但是假如用二分查找右端点得到结果时间复杂度n*logsum*logn,TLE,所以可以考虑用尺取法,所以总的复杂度为n*logn。
AC代码:
#include<stdio.h>
#include<math.h>
typedef long long ll;
ll a[100005],sum[100005];
ll cf2[40];
void init()
{
ll k = 1;
for(int i=0; i<=34; ++i)
{
cf2[i] = (k<<(i));
}
}
int main()
{
init();
ll T;
scanf("%lld",&T);
while(T--)
{
ll n;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
sum[n+1]=(ll)1E17;
ll ans=0,gg;
for(int i=1;i<=n;++i)
{
int p=i;
while(sum[p]==sum[i-1]&&p<=n+1)++p;
--p;
if(p>=i)ans+=(ll)(i*3+p)*(ll)(p-i+1)/2;
}
ll now=1;
ll last=0;
for(int j=0;j<=33;j++)
{
int p1=0,p2=0;
for(int i=1; i<=n; ++i)
{
ll adc = sum[i-1] + cf2[j];
while(sum[p1]<adc && p1<=n)++p1;
adc = sum[i-1] +cf2[j+1];
while(sum[p2]<adc && p2<=n+1)++p2;
--p2;
if(p2>=p1&&p2)ans+=(j+1)*(ll)(i*2+p1+p2)*(ll)(p2-p1+1)/2;
}
}
printf("%lld\n",ans);
}
return 0;
}