2017西安交大ACM小学期数据结构 [树状数组 离散化]

Problem E

发布时间: 2017年6月28日 12:53   最后更新: 2017年6月29日 21:35   时间限制: 1000ms   内存限制: 64M

给定一个长度为n的序列a1a2, ..., an

给定两个整数LR

输出有多少个二元组(x,y),xy, 满足Lyi=xaiR

9×104n105109ai109109LR109

第一行三个整数nLR, 意义如上所述。
第二行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 5
 3 -1 4 
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值