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
2015 Multi-University Training Contest 6


首先题意不是啥问题,就是给你个数组,让你求那个式子,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 
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]=p1 && p2)
                {
                    ans += (j+1)*(ll)(i*2+p1+p2)*(ll)(p2-p1+1)/2;
                }
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}




你可能感兴趣的:(不知道是啥算法的题)