1 2 1 1
12
计算上面给的那个公式。
比赛的时候想了个方法,以每个位置作为终点,二分log每种取值对应的起点,这样的复杂度是O(NlogN*35)左右,但是超时。这道题时间卡的很紧,复杂度O(N*35)的都要运行1000多ms。
枚举log不同取值的区间范围,在O(N)的复杂度下算出这个范围对应的所有i,j和。采用尺取法,假设以i为起始位置,对应[p1,p2]这个区间为终止位置,这些区间都是满足的,那么当i+1时,一定有终止位置p1'>=p1,p2'>=p2才可能满足。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<vector> #include<cmath> #include<queue> #include<stack> #include<map> #include<set> #include<algorithm> using namespace std; typedef long long LL; const LL MAXN=100010; const LL INF=0x3f3f3f3f; const LL SIGMA_SIZE=28; int T,N; int a[MAXN]; LL sum[MAXN]; //[l,r) i+j的和 LL solve(LL l,LL r){ LL ret=0; int p1=1,p2=1; for(int i=1;i<=N;i++){ if(p1<i) p1=i; if(p2<i) p2=i; while(p1<=N&&sum[p1]-sum[i-1]<l) p1++; while(p2<=N&&sum[p2]-sum[i-1]<r) p2++; p2--; if(p1<=p2&&sum[p1]-sum[i-1]>=l&&sum[p2]-sum[i-1]<r){ LL n=p2-p1+1; ret+=i*n+p1*n+n*(n-1)/2; } } return ret; } int main(){ freopen("in.txt","r",stdin); scanf("%d",&T); while(T--){ scanf("%d",&N); sum[0]=0; for(int i=1;i<=N;i++){ scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } LL ans=0; ans+=solve(0,1); for(int i=0;i<35;i++){ ans+=solve(1LL<<i,1LL<<(i+1))*(i+1); } printf("%I64d\n",ans); } return 0; }