题目: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:
log2
∑i=1n∑j=in(⌊log2S(i,j)⌋+1)×(i+j)
Note: In this problem, you can consider log2(0) as 0.
比赛时只想到n*logn*logn的做法,会T掉,必须写出nlogn的方法。
由于区间和在[1<<k,1<<(k+1)]之间的上式前部分总为k,枚举k,求区间和在[L,R)内的所有i+j之和,枚举区间起始位置i,用两个指针,一个标记从i开始的区间起点,一个标记从i开始的区间终点,这样即使i递增,两个指针也无需更新,因此复杂度是O(n).
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=100005;
int n;
ll d[maxn];
ll sum[maxn];
ll solve(ll L,ll R)
{
ll ans=0;
int l=1,r=0;
for(int i=1;i<=n;i++) {
if(l<i) l=i;
if(r<i-1) r=i-1;
while(l<=n && sum[l]-sum[i-1]<L) {
l++;
}
if(l==n+1)
break;
while(r<n && sum[r+1]-sum[i-1]<R) {
r++;
}
if(l>r)
continue;
ans+=i*(r-l+1);
ans+=(ll)(r-l+1)*(l+r)/2;
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++) {
scanf("%d",&d[i]);
sum[i]=sum[i-1]+d[i];
}
ll ans=0;
for(int i=1;i<35;i++) {
ans+=(ll)(i)*solve((1ll<<(i-1)),(1ll<<i));
}
ans+=solve(0,1);
printf("%lld\n",ans);
}
return 0;
}
log2