你须偿还(线段树)

莫斯提马躲进了城镇。菲亚梅塔正在追杀她。

城镇里有 n n n( 1 ≤ n ≤ 2 × 1 0 5 1≤n≤2×10^5 1n2×105)座修道院排成一行,第i座修道院的高度为 a i ( ∣ a i ∣ ≤ 1 0 9 ) a_i(|a_i|≤10^9) ai(ai109)

菲亚梅塔不想花时间寻找莫斯提马,她设定了两个参数 l , r l,r l,r( − 1 0 15 ≤ l ≤ r ≤ 1 0 1 5 −10^{15}≤l≤r≤10^15 1015lr1015)。

一段位置连续的修道院的高度之和在 [ l , r ] [l,r] [l,r]之内时,菲亚梅塔会直接来一发你须偿还。菲亚梅塔想知道自己一共会发射多少发你须偿还

形式化地说,求满足 l ≤ ∑ k = i j a k ≤ r l\le ∑_{k=i}^ja_k\le r lk=ijakr的二元组 ( l , r ) (l,r) (l,r)数量。

其中: 1 ≤ i ≤ j ≤ n 1≤i≤j≤n 1ijn

输入格式

第一行,三个正整数 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 lf(i)f(j1)r
我们对上式进行变化可以得到
f ( i ) − r ≤ f ( j − 1 ) ≤ f ( i ) − l f(i)-r \le f(j-1) \le f(i)-l f(i)rf(j1)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;
}

  • 29
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值