D. Petya and Array(树状数组 + 前缀和 + 逆序对的思想)

Problem - D - Codeforces

Petya 有一个由 n 个整数组成的数组 a。他最近学习了部分和,现在他可以非常快地计算出数组中任何一段元素的和。这个段是一个非空的序列,相邻的元素排在数组中。

现在他想知道他的数组中元素和小于 t 的段的数量。请帮助 Petya 计算这个数字。

更正式地说,你需要计算有多少对 l,r (l≤r),满足 al+al+1+⋯+ar−1+ar<t。

输入 第一行包含两个整数 n 和 t (1≤n≤200000,|t|≤2⋅1014)。

第二行包含一个整数序列 a1,a2,…,an (|ai|≤109) —— 描述 Petya 的数组。注意数组中可能有负数、零和正数元素。

输出 打印出 Petya 数组中元素和小于 t 的段的数量。

Examples

input

Copy

5 4
5 -1 3 4 -1

output

Copy

5

input

Copy

3 0
-1 2 -3

output

Copy

4

input

Copy

4 -1
-2 1 -2 3

output

Copy

3

 

在第一个例子中,以下段落的元素总和小于4:

  • [2, 2],元素总和为-1
  • [2, 3],元素总和为2
  • [3, 3],元素总和为3
  • [4, 5],元素总和为3
  • [5, 5],元素总和为-1

题解:
写本题前需要知道树状数组求逆序对的思想

树状数组的询问操作是,询问下标为1~x的的前缀和

插入操作是,在下标为x的数加上一个值p

(树状数组的核心思想)

#include<bits/stdc++.h>
#define M 500005
using namespace std;
int a[M],d[M],t[M],n;
int lowbit(int x)
{
	return x&-x;
}
int add(int x)//把包含这个数的结点都更新 
{
	while(x<=n)//范围 
	{
		t[x]++;
		x+=lowbit(x);
	}
}
int sum(int x)//查询1~X有几个数加进去了 
{
	int res=0;
	while(x>=1)
	{	
		res+=t[x];
		x-=lowbit(x);
	}
	return res;
}
bool cmp(int x,int y)//离散化比较函数 
{
	if(a[x]==a[y]) return x>y;//避免元素相同 
	return a[x]>a[y];//按照原序列第几大排列 
}
int main()//402002139
{
	//freopen("in.txt","r",stdin);
	long long ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i],d[i]=i;
	
	sort(d+1,d+n+1,cmp);//离散化 
	
	for(int i=1;i<=n;i++)
	{
		add(d[i]);//把这个数放进去 
		ans+=sum(d[i]-1);//累加 
	}
	cout<<ans;
	return 0;
}
 
 

求逆序对的代码

本题的思想,我们求出数组的前缀和

应该满足i > j 并且s[i] - s[j] < k,对于本题条件

i > j&&s[i] < k -s[j]这不就是求逆序对的思想吗

#include <cstdio>
#include <cstring>
#include <algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
typedef pair<int,int> PII;
const int N = 3e5 + 10;
int mod = 1e9 + 7;
int a[N];
int cnt[N];
int tre[N];
int n;
int lowbit(int x)
{
	return x & - x;
}
int sum(int x)
{
	int ans = 0;
	for(int i = x;i;i -= lowbit(i))
	{
		ans += tre[i];
	}
	return ans;
}
void add(int x,int p)
{
	for(int i = x;i <= n + 1;i += lowbit(i))
	{
		tre[i] += p;
	}
}
void solve()
{
	int k;
	cin >> n >> k;
	for(int i = 2;i <= n + 1;i++)//从下标2起使树状数组下标不会为0 
	{
		cin >> a[i];
		a[i] = a[i - 1] + a[i];
		cnt[i] = a[i];
	}
	sort(cnt + 1,cnt + n + 2);//为什么包括cnt[1]?因为sumn[1]什么都不选也是一种前缀 
	int ans = 0;
	for(int i = 1;i <= n + 1;i ++)
	{
		int pos = upper_bound(cnt + 1,cnt + 2 + n,a[i] - k) - cnt;//第一个大的是第几名 
		ans += i - 1 - sum(pos - 1);//i-1个区间,去掉不满足的get(pos-1)
		pos = lower_bound(cnt + 1,cnt + 2 + n,a[i]) - cnt;//填自己原来的位置去 
		add(pos,1);//存入数状数组 
	}
	cout << ans;
}

signed main()
{
//	ios::sync_with_stdio(0 );
//	cin.tie(0);cout.tie(0);
	int t = 1;
//	cin >> t;
	while(t--)
	{
		solve(); 
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值