HDU 5358 多校第6场 First One

First One

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 672    Accepted Submission(s): 193


Problem Description
soda has an integer array a1,a2,,an. Let S(i,j) be the sum of ai,ai+1,,aj. Now soda wants to know the value below:
i=1nj=in(log2S(i,j)+1)×(i+j)

Note: In this problem, you can consider log20 as 0.
 

Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1n105), the number of integers in the array.
The next line contains n integers a1,a2,,an (0ai105).
 

Output
For each test case, output the value.
 

Sample Input
1 2 1 1
 

Sample Output
12
 

Source


首先题意不是啥问题,就是给你个数组,让你求那个式子,s表示i到j的和。

比赛的时候想了个带二分的,就是统计log2可能出现的可能,一共同拥有34种,然后求一个前缀和,前缀和是递增的,能够二分。就写了

复杂度没算好,以为33*n*log(n)能过。结果跪了一下午,昨天就这么愉快的跪了

赛后题解上说用俩指针扫一遍,不得不吐槽一下。MD题解都是英文的。

联想到一次bestcoder 有一个题是求一共序列中两个数的和模上一个数最大,我以前的做法就是排序二分。而正解就是俩指针扫一遍,然而我没有记住

这个题就卡这个log(n)然而我就是过不去,诶。还是思维被限制住了

今天上午搞了个33 * n的

思路:

把数组处理成 前缀和,把log分类,有35种情况,题中吧log(0) 规为0。须要特判,题中给的最大的sum值为10^10,所以有35种情况,

那么就分情况讨论,一层循环。里面就是枚举n。然后找到两个值一个是sum[i-1] + 2^j的位置。另一个是sum[i-1]+2^(j+1)的位置,这段区间内的log值就全为j,用等差数列求和公式就能瞬间算出来这段区间的值,并且仅仅须要两个标记扫一遍。为何能够仅仅扫一遍呢,sum数组的值是递增的。须要找的值也是递增的。所下面次找的时候仅仅须要在上次寻找的后面继续接上就OK

/****************************************
** 2015 Multi-University Training Contest 6
** 1006 First One
** HDU 5358
** by calamity_coming
**************************************/

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAX_LONG = 1E5;
ll   a[MAX_LONG + 10];
ll sum[MAX_LONG + 10];
ll cf2[40];

void init()
{
    ll k = 1;
    for(int i=0; i<=34; ++i)
    {
        cf2[i] = (k<<(i));
    }
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        sum[0] = 0;
        for(int i=1; i<=n; ++i)
        {
            scanf("%I64d",&a[i]);
            sum[i] = sum[i-1] + a[i];//前缀和处理
        }
        sum[n+1] = 1E17;
        ll ans = 0;

        //0
        for(int i=1; i<=n; ++i)//0比較特殊。要特殊的干
        {
            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;
            }
        }

        for(int j=0; j<=33; ++j)//这是每一个情况
        {
            int p1 = 0,p2 = 0;//用两个指针扫一遍
            for(int i=1; i<=n; ++i)//当i递增时,新的p1,p2一定在后面
            {
                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("%I64d\n",ans);
    }
    return 0;
}




posted @ 2017-05-25 20:50  yfceshi  阅读(189)  评论(0编辑  收藏  举报