题目链接:
解析:
-
计算前缀和数组:首先,我们计算一个前缀和数组
s
,其中s[i]
表示从数组a
的第一个元素到第i
个元素的和。这样,任意子数组的和可以通过前缀和数组快速计算得出。 -
遍历每个可能的左端点:我们从数组的第一个元素开始,遍历每个可能的左端点
i
(从 1 到n-2
,因为我们需要至少有一个元素在中间段和右段)。 -
二分查找中间点:对于每个左端点
i
,我们使用二分查找来找到合适的中间点l
。二分查找的范围是从i+1
到n-1
。 -
检查中间段的和:在二分查找的过程中,我们检查当前中间点
mid
是否满足条件:s[mid] - s[i]
(中间段的和)大于左段的和s[i]
以及右段的和s[n] - s[mid]
。如果满足,我们就缩小右边界r
;如果不满足,我们就增大左边界l
。 -
验证找到的中间点:当二分查找结束时,我们得到一个可能的中间点
l
。我们需要验证这个点是否真的满足条件。如果满足,那么从l
到n-1
的每个元素都可以作为右端点与左端点i
形成满足条件的子数组。 -
更新结果:对于每个满足条件的中间点
l
,我们计算从l
到n-1
的元素个数(即n - l
),并将其累加到结果res
中。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
long long n,ans;
long long a[N],pre[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=pre[i-1]+a[i];
}
for(int i=1;i<=n-2;i++){
int l=i+1,r=n-1;
while(l<r){
int mid=(l+r)>>1;
if(pre[mid]-pre[i]>pre[i]&&pre[mid]-pre[i]>pre[n]-pre[mid]){
r=mid;
}
else
l=mid+1;
}
if(pre[l]-pre[i]>pre[i]&&pre[l]-pre[i]>pre[n]-pre[l]){
ans+=n-l;
}
}
cout<<ans<<endl;
return 0;
}