标签:美团
//给一个序列,求其所有子序列s(i,j)(i<=j)的对2取对数的再向下取整+1在乘以(i+j)的和
//枚举区间[1<<(k-1) , 1<<k),再枚举左边区间,找到右边区间的范围
//那么ans = segma(((r-l)*i + (r-l)*(r-1+l)/2)*k)(1<=k<=34)再特判断0就行 ;
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std ;
const int maxn = 100010 ;
typedef __int64 ll ;
ll sum[maxn] ;
int n,t ;
ll find(ll L , ll R)
{
ll ans = 0 ;
ll r = 0 , l = 0 ;
for(int i = 1;i <= n;i++)
{
if(l < i)l = i ;
if(r < i)r = i ;
while(sum[l] - sum[i-1] < L && l <= n)l++ ;
while(sum[r] - sum[i-1] < R && r <= n)r++;
if(l >= r)continue ;
ans += (r-l)*((ll)i) + (r-l)*(r-1+l)/(ll)2 ;
}
return ans ;
}
int main()
{
//freopen("in.txt" ,"r" , stdin) ;
scanf("%d" , &t) ;
while(t--)
{
scanf("%d" , &n) ;
sum[0] = 0 ;
for(int i = 1;i <= n;i++)
scanf("%I64d" , &sum[i]) , sum[i] += sum[i-1] ;
ll ans = 0 ;
ll l , r ;
for(int k = 1;k <= 34;k++)
{
l = (ll)1<<(k-1) ;
r = (ll)1<<k ;
ans += find(l,r)*k ;
}
ans += find(0,1) ;
printf("%I64d\n" ,ans) ;
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:美团
原文地址:http://blog.csdn.net/cq_pf/article/details/47336177