Petya and Array CodeForces - 1042D(权值线段树)

本文介绍了一种使用前缀和与权值线段树解决区间和查询问题的方法,具体为求解数组中元素和小于特定值的子数组数量。通过离散化处理,实现了高效查询。

Petya has an array aa consisting of nn integers. He has learned partial sums recently, and now he can calculate the sum of elements on any segment of the array really fast. The segment is a non-empty sequence of elements standing one next to another in the array.

Now he wonders what is the number of segments in his array with the sum less than tt. Help Petya to calculate this number.

More formally, you are required to calculate the number of pairs l,rl,r (l≤rl≤r) such that al+al+1+⋯+ar−1+ar<tal+al+1+⋯+ar−1+ar<t.

Input
The first line contains two integers nn and tt (1≤n≤200000,|t|≤2⋅10141≤n≤200000,|t|≤2⋅1014).

The second line contains a sequence of integers a1,a2,…,ana1,a2,…,an (|ai|≤109|ai|≤109) — the description of Petya’s array. Note that there might be negative, zero and positive elements.

Output
Print the number of segments in Petya’s array with the sum of elements less than tt.

Examples
Input
5 4
5 -1 3 4 -1
Output
5
Input
3 0
-1 2 -3
Output
4
Input
4 -1
-2 1 -2 3
Output
3
Note
In the first example the following segments have sum less than 44:

[2,2][2,2], sum of elements is −1−1
[2,3][2,3], sum of elements is 22
[3,3][3,3], sum of elements is 33
[4,5][4,5], sum of elements is 33
[5,5][5,5], sum of elements is −1
对于这种求区间和的问题,一般会转化成前缀和的问题来求.
对于当前值sum[i]来说,要找它之后的区间和有多少个小于v,只需要找有多少个sum[k]<sum[i]+v就行,这样就转化成了某个数之后有多少个数小于一个数,这样就可以用权值线段树来解决了.因为数据很大,需要离散化一下.
代码如下:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm> 
#define ll long long
using namespace std;

const int maxx=4e5+100;//因为我们要把sum[i]和sum[i]+v都记录下来,所以就应该是原来数据量二倍的数据
ll sum[maxx],a[maxx],b[maxx];
struct node{
	int l;
	int r;
	ll v;
}p[maxx<<2];
ll n,ans;
ll v;

inline void pushup(int cur)
{
	p[cur].v=p[cur<<1].v+p[cur<<1|1].v;
}
inline void build(int l,int r,int cur)
{
	p[cur].l=l;
	p[cur].r=r;
	p[cur].v=0;
	if(l==r) return ;
	int mid=l+r>>1;
	build(l,mid,cur<<1);
	build(mid+1,r,cur<<1|1);
}
inline void update(int pos,int cur)
{
	int L=p[cur].l;
	int R=p[cur].r;
	if(L==R)
	{
		p[cur].v+=1;
		return ;
	}
	int mid=L+R>>1;
	if(pos<=mid) update(pos,cur<<1);
	else update(pos,cur<<1|1);
	pushup(cur);
}
inline void query(int pos,int cur)
{
	int L=p[cur].l;
	int R=p[cur].r;
	if(L==R) return ;
	int mid=L+R>>1LL;
	if(pos<=mid) query(pos,cur<<1);
	else 
	{
		ans+=p[cur<<1].v;
		query(pos,cur<<1|1);
	}
}
int main()
{
	while(~scanf("%d%lld",&n,&v))
	{
		int cnt=0;ans=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			sum[i]=sum[i-1]+a[i];
			b[++cnt]=sum[i];
		}
		if(n==1)//一的时候特判
		{
			if(a[1]<v) puts("1");
			else puts("0");
		}
		else
		{
			for(int i=1;i<=n;i++) b[++cnt]=sum[i]+v;
			sort(b+1,b+1+cnt);
			int len=unique(b+1,b+1+cnt)-b-1;
			build(1,len,1);
			update(lower_bound(b+1,b+1+len,sum[n])-b,1);//先将最后的一个插入进去
			for(int i=n-1;i>=0;i--)
			{
				ll x=sum[i]+v;
				query(lower_bound(b+1,b+1+len,x)-b,1);//在查询的时候用的是sum[i]+v
				if(i) update(lower_bound(b+1,b+1+len,sum[i])-b,1);//插入的时候用的是sum[i]
			}
			printf("%lld\n",ans);
		}
	}
	return 0;
}

努力加油a啊,(o)/~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

starlet_kiss

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

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

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

打赏作者

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

抵扣说明:

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

余额充值