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();
}
}