题目链接:传送门
题意:设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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-17 11:19:56