题目链接:传送门
题意:设f(i,j)表示区间[i,j]内元素的和 ,定义 SUM(i,j) = [log2(f(i,j))+1]*(i+j)
求 sigma(sum (i,j)) ( 1<=i<=n,i<=j<=n )
分析: log2(f(i,j))表示f(i,j)转换为2进制的长度,然后我们经过分析log2(f(i,j))+1的值域
为[1,34]然后我们枚举log2(f(i,j))+1的值,例如我们枚举其值为k,对于一个k我们找到所有满足
条件的区间(i,j),这个条件的代数表达为 2^(k-1)<= f(i,j) <=2^k-1;
因此我们需要再枚举一个区间的左端点,对于一个给定的左端点,因为f(i,j)在给定i的情况下单调,
我们可以用尺举发求得一个区间[l,r],使得区间内的j (l<=j<=r)都瞒住sum(i,j)+1=k;
然后区间(i+j)的和可以表示为 i*(r-l+1) + (r+l)*(r-l+1)/2;
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e5+10;
typedef long long LL;
LL sum[maxn];
int main()
{
int t,n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++){
LL x;
scanf("%I64d",&x);
sum[i]=sum[i-1]+x;
}
LL ans = 0;
for(LL k = 1;k<=34;k++){
LL l=1,r=0;
LL lmax = 1LL<<(k-1),rmax=(1LL<<k)-1;
if(k==1) lmax = 0;
for(LL i=1;i<=n;i++){
l=max((LL)i,l);
while(l<=n&&sum[l]-sum[i-1]<lmax) l++;
r=max(l-1,r);
while(r+1<=n&&sum[r+1]-sum[i-1]<=rmax&&sum[r+1]-sum[i-1]>=lmax)r++;
if(l>r) continue;
ans=ans+(i*(r-l+1)+(r+l)*(r-l+1)/2)*k;
}
}
printf("%I64d\n",ans);
}
return 0;
}