hdu5358First One 枚举

//给一个序列,求其所有子序列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) ;
    }
}






你可能感兴趣的:(美团)