Problem E
发布时间: 2017年6月28日 12:53 最后更新: 2017年6月29日 21:35 时间限制: 1000ms 内存限制: 64M
给定一个长度为n的序列a1, a2, ..., an
给定两个整数L, R
输出有多少个二元组(x,y),x≤y, 满足L≤∑yi=xai≤R
9×104≤n≤105, −109≤ai≤109, −109≤L≤R≤109
第一行三个整数n, L, R, 意义如上所述。
第二行n个整数, 表示序列a。
一个数, 表示答案。
8 -6 6 3 -1 4 -1 5 -9 2 -6
28
要求区间和处于区间[L,R]之间的不同的区间有多少个。
看到要求区间和的问题,立刻想到树状数组。
但是现在有两个比较棘手的问题需要处理
(1)负数下标问题(树状数组的下标不能为负数,但是我们处理区间和的时候有可能产生负数)
(2)区间和范围太大,直接开辟如此大的树状数组肯定会MLE
因此,我们考虑这样的方法,那就是离散化!
我们解决这道题目的总体上的思路就是
for循环sum[1...i]
然后判断有多少个sum[1...j] (j <= i)使得sum[1....i] - sum[1...j] 在区间[L,R]内
等价于L<=sum[i]-sum[j]<=R 等价于 sum[i]-R<=sum[j]<=sum[i]-L
这样的话,我们把sum[i] (1<=i<=n)进行离散化(最多1e5个值)
我们用二分搜索找出sum[i]-R和sum[i]-L在离散化后的数列中的位置,也就是在树状数组中的位置。
然后直接求一个区间和就好了。
注意别忘了for循环体在一开始把sum[i-1]对应的离散值加入到树状数组里面去
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL MAX = 1e5 + 7;
const int N = 1e7 + 7;
LL a[MAX],b[N];
int n;
LL sm[MAX];
LL dis[MAX];
int mp[MAX];
LL L,R;
inline LL lowbit(int x){return x & (-x);
}
LL getsum(int pos){LL res = 0;while(pos){res += b[pos];pos -= lowbit(pos);}return res;
}
void add(int pos,LL val){while(pos <= N){b[pos] += val;pos += lowbit(pos);}
}
int main(){scanf("%d%lld%lld",&n,&L,&R);for(int i = 1;i <= n;i++){scanf("%lld",&a[i]);//a[i] += stdi;dis[i-1] = sm[i] = sm[i-1] + a[i];}sort(dis,dis+n);int k = unique(dis,dis+n) - dis ;//cout<<k<<endl;for(int i = 1;i <= n;i++)mp[i] = lower_bound(dis,dis+n,sm[i]) - dis + 1;LL ans = 0;if(a[1] >= L && a[1] <= R) ans++;for(int i = 2;i <= n;i++){if(sm[i] >= L && sm[i] <= R) ans++;add(mp[i-1],1);LL x = sm[i] - R ;LL y = sm[i] - L;int idx = lower_bound(dis,dis+n,x) - dis ;int idy = lower_bound(dis,dis+n,y) - dis ;if(dis[idy] == y) idy ++;ans += getsum(idy) - getsum(idx);//printf("%d\n",getsum(idy) - getsum(idx));}printf("%lld\n",ans);return 0;
}
/*
3 0 0 0 0 0 3 -5 53 -1 4
*/