【bzoj4627】[BeiJing2016]回转寿司 离散化+树状数组
题目描述
给出一个长度为n的序列,求所有元素的和在[L,R]范围内的连续子序列的个数。
输入
第一行包含三个整数N,L和R,分别表示寿司盘数,满意度的下限和上限。
第二行包含N个整数Ai,表示小Z对寿司的满意度。
N≤100000,|Ai|≤100000,0≤L, R≤10^9
输出
仅一行,包含一个整数,表示共有多少种选择可以使得小Z的满意度之和
不低于L且不高于R。
样例输入
5 5 9
1 2 3 4 5
样例输出
6
题解
离散化+树状数组
把序列和转化为前缀相减,即选出满足$L\le sum[x]-sum[y]\le R$的$x>y$的数对个数。
那么我们枚举$x$,即可得到$y$的范围,要求的是以前的满足条件的$y$的个数。可以维护1到当前位置树状数组,在树状数组中查询个数,最后再把该数加入到树状数组中。由于数据范围大,因此需要离散化。
时间复杂度$O(n\log n)$
#include <cstdio> #include <algorithm> #define N 100010 #define now v + 1 , v + n + 2 using namespace std; typedef long long ll; ll sum[N] , v[N]; int f[N] , n; inline void add(int x) { int i; for(i = x ; i <= n + 1 ; i += i & -i) f[i] ++ ; } inline int query(int x) { int i , ans = 0; for(i = x ; i ; i -= i & -i) ans += f[i]; return ans; } int main() { int i; ll l , r , ans = 0; scanf("%d%lld%lld" , &n , &l , &r); for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &sum[i]) , sum[i] += sum[i - 1] , v[i] = sum[i]; sort(now); for(i = 0 ; i <= n ; i ++ ) ans += query(upper_bound(now , sum[i] - l) - v - 1) - query(lower_bound(now , sum[i] - r) - v - 1) , add(lower_bound(now , sum[i]) - v); printf("%lld\n" , ans); return 0; }