莫斯提马躲进了城镇。菲亚梅塔正在追杀她。
城镇里有 n n n( 1 ≤ n ≤ 2 × 1 0 5 1≤n≤2×10^5 1≤n≤2×105)座修道院排成一行,第i座修道院的高度为 a i ( ∣ a i ∣ ≤ 1 0 9 ) a_i(|a_i|≤10^9) ai(∣ai∣≤109)。
菲亚梅塔不想花时间寻找莫斯提马,她设定了两个参数 l , r l,r l,r( − 1 0 15 ≤ l ≤ r ≤ 1 0 1 5 −10^{15}≤l≤r≤10^15 −1015≤l≤r≤1015)。
当一段位置连续的修道院的高度之和在
[
l
,
r
]
[l,r]
[l,r]之内时,菲亚梅塔会直接来一发你须偿还
。菲亚梅塔想知道自己一共会发射多少发你须偿还
。
形式化地说,求满足 l ≤ ∑ k = i j a k ≤ r l\le ∑_{k=i}^ja_k\le r l≤∑k=ijak≤r的二元组 ( l , r ) (l,r) (l,r)数量。
其中: 1 ≤ i ≤ j ≤ n 1≤i≤j≤n 1≤i≤j≤n
输入格式
第一行,三个正整数 n , l , r n,l,r n,l,r。
第二行, n n n个整数 a i a_i ai,表示修道院的高度。
输出格式
输出一个数,表示你的答案。
样例
input
6 1 5
1 1 4 5 1 4
output
9
input
10 -3 5
2 -1 4 -7 4 8 -3 -6 -4 7
output
32
最简单的方式是暴力搜索,但是会TLE
需要想办法进行批量处理
解题思路如下:
首先处理得到前缀和,通过前缀和可以把区间求和转化为端点差值
那么题目的条件变为
l
≤
f
(
i
)
−
f
(
j
−
1
)
≤
r
l \le f(i)-f(j-1)\le r
l≤f(i)−f(j−1)≤r
我们对上式进行变化可以得到
f
(
i
)
−
r
≤
f
(
j
−
1
)
≤
f
(
i
)
−
l
f(i)-r \le f(j-1) \le f(i)-l
f(i)−r≤f(j−1)≤f(i)−l
枚举右端点,则左端点的区间范围是确定的
我们采用线段树+离散化的方式,就可以在logn时间内得到可能的左端点的数量
时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
以下是离散化的思路,不需要可以自行跳过:
将处理前缀和数组,得到离散化数组
对于每个右端点,计算查询区间的左右端点值,然后在原数组中二分查找对应的值,从而获取离散化后的查询区间
AC代码如下
#include <iostream>
#include <algorithm>
#include <map>
#define int long long
using namespace std;
const int max_n = 2e5;
const long long max_num = 1e16;
typedef struct {
int val, idx;
}node;
int n, l, r, arr[max_n + 1], pre[max_n + 1];
node tmparr[max_n + 1];
map<int, int>dict;
int tree[max_n * 4], lazy_tag[max_n * 4];
inline int read() {
int x = 0, y = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') y = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * y;
}
int discrete() {
sort(tmparr, tmparr + 1 + n, [](node n1, node n2) {
return n1.val < n2.val;
});
int cnt = 0, tmpval = 0;
tmparr[0].idx = ++cnt;
tmpval = tmparr[0].val;
dict[tmpval] = cnt;
for (int i = 1; i <= n; i++) {
if (tmparr[i].val == tmpval) tmparr[i].idx = cnt;
else {
tmparr[i].idx = ++cnt;
tmpval = tmparr[i].val;
dict[tmpval] = cnt;
}
}
return cnt;
}
void push_down(int idx, int l, int r) {
if (lazy_tag[idx]) {
int tmp = lazy_tag[idx];
tree[idx << 1] += tmp;
tree[idx << 1 | 1] += tmp;
lazy_tag[idx << 1] += tmp;
lazy_tag[idx << 1 | 1] += tmp;
lazy_tag[idx] = 0;
}
}
void update(int idx, int l, int r, int L, int R, int val) {
if (L <= l && r <= R) {
tree[idx] += val;
lazy_tag[idx] += val;
return;
}
push_down(idx, l, r);
int mid = (l + r) / 2;
if (L <= mid) update(idx << 1, l, mid, L, R, val);
if (R >= mid + 1) update(idx << 1 | 1, mid + 1, r, L, R, val);
tree[idx] = tree[idx << 1] + tree[idx << 1 | 1];
}
int query(int idx, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return tree[idx];
}
push_down(idx, l, r);
int mid = (l + r) / 2;
int ret = 0;
if (L <= mid) ret += query(idx << 1, l, mid, L, R);
if (R >= mid + 1) ret += query(idx << 1 | 1, mid + 1, r, L, R);
return ret;
}
bool judge(int idx, int val) {
return tmparr[idx].val < val;
}
int bin_search(int val) {
int l = -1, r = n + 1;
int mid;
while (l + 1 != r) {
mid = (l + r) / 2;
if (judge(mid, val)) l = mid;
else r = mid;
}
return l;
}
signed main() {
n = read(); l = read(); r = read();
for (int i = 1; i <= n; i++) {
arr[i] = read();
tmparr[i].val = pre[i] = pre[i - 1] + arr[i];
}
int cnt = discrete();
int ans = 0;
for (int i = 0; i <= n; i++) { //注意需要从0开始
int lval = pre[i] - r;
int rval = pre[i] - l;
int llimit = bin_search(lval) + 1;
int rlimit = bin_search(rval + 1);
if (!(llimit > n || rlimit == -1)) {
llimit = tmparr[llimit].val;
rlimit = tmparr[rlimit].val;
llimit = dict[llimit];
rlimit = dict[rlimit];
ans += query(1, 1, cnt, llimit, rlimit);
}
int p = dict[pre[i]];
update(1, 1, cnt, p, p, 1);
}
printf("%lld", ans);
return 0;
}