Codeforces Round #510 (Div. 2) D. Petya and Array (权值线段树)
题目地址:http://codeforces.com/contest/1042/problem/D
题意:给你n个数,问有多少个区间的和的值小于t
分析:区间和问题,常常用到前缀和来进行预处理,所以先预处理出前缀和数组sum
sum[i]代表前i个数的和,那么sum[i]的贡献就是, 当i<k<=n时,存在多少个k,使sum[k]<t+sum[i]
也就是求在[i+1,n]中,小于t+sum[i]的数有多少。
所以我们可以类比于询问一个数是区间第几大的方法,使用权值线段树来解决,这里因为数的范围较大,首先需要进行离散化处理。
代码如下:
#include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> #include <vector> #include <cstring> #include <map> using namespace std; const int N=2e5+100; typedef long long LL; vector<LL>V; int T[N<<3]; LL res; LL sum[N]; LL a[N]; LL n,t; int get_id(LL x) //离散化处理 { return lower_bound(V.begin(),V.end(),x)-V.begin()+1; } void BuildTree(int rt,int l,int r) { T[rt]=0; if(l==r)return; int mid=(l+r)>>1; BuildTree(rt<<1,l,mid); BuildTree(rt<<1|1,mid+1,r); } void Updata(int p,int v,int rt,int l,int r) //插入v个值为p的数,若v为负数,即为删去. { T[rt]+=v; if(l==r)return; int mid=(l+r)>>1; if(p<=mid)Updata(p,v,rt<<1,l,mid); else Updata(p,v,rt<<1|1,mid+1,r); } void less_num(int pos,int rt,int l,int r) { if(l==r) return; int mid=(l+r)>>1; if(pos<=mid) less_num(pos,rt<<1,l,mid); else { res+=T[rt<<1]; less_num(pos,rt<<1|1,mid+1,r); } } int main() { scanf("%lld%lld",&n,&t); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; V.push_back(sum[i]); } if(n==1) { if(a[1]<t) printf("1\n"); else printf("0\n"); return 0; } for(int i=1;i<=n;i++) { LL x=sum[i]+t; V.push_back(x); } sort(V.begin(),V.end()); V.erase(unique(V.begin(),V.end()),V.end()); int len=V.size(); BuildTree(1,1,len); Updata(get_id(sum[n]),1,1,1,len); for(int i=n-1;i>=0;i--) { LL x=sum[i]+t; less_num(get_id(x),1,1,len); if(i>0) Updata(get_id(sum[i]),1,1,1,len); } printf("%lld\n",res); return 0; }