题意:给你n个数,问有多少个区间的和的值小于t
分析:区间和问题,常常用到前缀和来进行预处理,所以先预处理出前缀和数组sum
sum[i]代表前i个数的和,那么sum[i]的贡献就是, 当i<k<=n时,存在多少个k,使sum[k]<t+sum[i],也就是求在[i+1,n]中,小于t+sum[i]的数有多少。
所以我们可以类比于询问逆序对方法,使用权值线段树来解决,这里因为数的范围较大,首先需要进行离散化处理。
AC_Code:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 2e5+10; 5 #define rep(i,first,last) for(int i=first;i<=last;i++) 6 #define dep(i,first,last) for(int i=first;i>=last;i--) 7 vector<ll>v; 8 int tree[maxn<<3]; 9 ll sum[maxn],a[maxn],n,t,ans; 10 11 int getid(ll x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;} 12 void updata(int rt,int l,int r,int pos){ 13 if( l==r ){ 14 tree[rt]++; 15 return ; 16 } 17 int mid=(l+r)>>1; 18 if( pos>mid ) updata(rt<<1|1,mid+1,r,pos); 19 else updata(rt<<1,l,mid,pos); 20 tree[rt]=tree[rt<<1]+tree[rt<<1|1]; 21 } 22 23 ll query(int rt,int l,int r,int val){ 24 if( l==r ) return tree[rt]; 25 int mid=(l+r)>>1; 26 ll f=0; 27 if( val>mid ) return f+=tree[rt<<1]+query(rt<<1|1,mid+1,r,val); 28 else return f+=query(rt<<1,l,mid,val); 29 return f; 30 } 31 32 int main(){ 33 scanf("%lld%lld",&n,&t); 34 rep(i,1,n){ 35 scanf("%lld",&a[i]); 36 sum[i]=sum[i-1]+a[i]; 37 v.push_back(sum[i]); 38 } 39 if(n==1){ 40 if( a[1]<t ) printf("1 "); 41 else printf("0 "); 42 return 0; 43 } 44 rep(i,0,n){ //注意从零开始 45 ll x=sum[i]+t; 46 v.push_back(x-1); 47 } 48 sort(v.begin(),v.end()); 49 v.erase(unique(v.begin(),v.end()),v.end()); 50 int len=v.size(); 51 updata(1,1,len,getid(sum[n])); 52 dep(i,n-1,0){ //注意到0截止 53 ll x=sum[i]+t; 54 ans+=query(1,1,len,getid(x-1)); 55 updata(1,1,len,getid(sum[i])); 56 } 57 printf("%lld ",ans); 58 return 0; 59 }