First One
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1595 Accepted Submission(s): 488
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:
Note: In this problem, you can consider log20 as 0.
∑i=1n∑j=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 (1≤n≤105) , the number of integers in the array.
The next line contains n integers a1,a2,…,an (0≤ai≤105) .
The first line contains an integer n (1≤n≤105) , the number of integers in the array.
The next line contains n integers a1,a2,…,an (0≤ai≤105) .
Output
For each test case, output the value.
Sample Input
1 2 1 1
Sample Output
12
思路 :传说中的尺取法
就是在一段范围内不断地找出符合条件的区间,通过不断右移左区间,来找到右区间。
而这道题,如果直接暴力的话,肯定会超时,遇到这类题目一定要找到数目的范围想办法。
10^10 ~~~log2(1-34) 发现如果根据i和j找到log2(sum[i,j]) 的范围超时,但是如果根据
log2(x)找出所有符合条件的i 和j 不会超时 在每一个i 对应的范围内 【l,r】都符合x
推算一下计算公式计算就可以了。
这几场多校,算法不是很难,最重要的还是思路!!!
代码:
/*踏实!!努力!!*/
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<map>
#include<queue>
#include<stack>
using namespace std;
#define N 100005
#define LL __int64
int data[N];
LL sum[N];
int main()
{
LL ans,pl,pr;
int t,n,m;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
sum[0]=0;
ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&data[i]);
sum[i]=sum[i-1]+data[i];
}
if(!sum[n]) m=1;
else m=(int)log2(sum[n]*1.0)+1;
for(int i=1;i<=m;i++){
int l=1,r=0;
if(i==1) pl=0;
else pl=(LL)1<<(i-1);
pr=(LL)1<<i; //注意(LL)1
for(int j=1;j<=n;j++){
l=max(l,j);
//找到左区间
while(sum[l]-sum[j-1]<pl&&l<=n)
l++;
r=max(l-1,r);
//找到右区间
while(r+1<=n&&sum[r+1]-sum[j-1]<pr&&sum[r+1]-sum[j-1]>=pl)
r++;
//计算公式 (log2(j,[l,r])+1)*(j+[l,r])
// = i* (j+[l,r]) = i* j *(r-l+1) + i*(l,r)
if(l<=r)
ans+=(LL)(r-l+1)*(l+r)*i/2+(LL)j*(r-l+1)*i;
}
}
printf("%I64d\n",ans);
}
return 0;
}