原题链接 https://www.jisuanke.com/course/1797/165800
题解:这是一道比较经典的树状数组的题 。
有道题是求区间均值大于k的区间数,和这道题思路差不多,我也是看来这道题的题解才搞懂这题怎么做的,具体参考
https://blog.csdn.net/scutzyz126/article/details/78040539
这道题就是求区间[l,r]的区间和小于m,翻译成前缀和就是sum[r]-sum[l-1] < m,
假定l>=1,引入sum[0] = 0,
移项后就是sum[r] < m + sum[l-1],
因为l<=r,所以l-1<r,
给一个例子,比如sum数组为0,1,-1,2,4,m=2
相当于求有多少对数,满足前面的数加上m大于后面的数,
比如sum[1]和sum[3]就满足,因为1+2 > 2,
如果暴力遍历所有数对,时间复杂度肯定是n^2,
所以需要用树状数组来统计,
因为只关心数之间大小的相对关系,不关心数的具体值,所以对所有的数进行离散化,给他们排名,
所以把原来的sum数组0,1,-1,2,4和每个数加2后的数组2,3,1,4,6加在一块排序再去重(unique)
然后就是-1,0,1,2,3,4,6,
这7个数的排名就是1到7,
最后就从右到左边遍历sum数组的5个数,每次遍历的时候会把当前数的排名所在的地方+1,比如遍历完-1这个数的时候,-1,2,4排名1,4,6的地方就加了1,遍历到1的时候,统计Sum(rank(1+2))=Sum(5),即当前排名小于等于5的数的个数,但是因为求的数不包含等于的结果,所以应该统计Sum(rank(3)+1)=Sum(6),这样就把-1,2,4这3个数中,
小于1+m的数的个数统计出来了,然后遍历一遍就算出结果了。
这题需要注意的一个小坑就是统计结果得是long long,用int只能过40%的数据
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 7e5;
ll m;
ll a[MAXN];
int n;
ll s[MAXN];
ll temp[MAXN];
ll Rank[MAXN];
ll tree[MAXN];
int Count;
int lowbit(int x) {
return x & -x;
}
// 排名从1开始
// x处加1
void Add(int x) {
for(int i = x;i <= Count;i += lowbit(i)) {
tree[i] += 1;
}
}
// 1...x的和
int Sum(int x) {
int res = 0;
for(int i = x;i >= 1;i -= lowbit(i)) {
res += tree[i];
}
return res;
}
int main()
{
scanf("%d%lld",&n,&m);
for(int i = 1;i <= n;i++) {
scanf("%lld", &a[i]);
s[i] = s[i-1] + a[i];
temp[i] = s[i];
}
// s[0]...s[n]
for(int i = n+1;i <= 2*n+1;i++) {
temp[i] = s[i-n-1] + m;
}
sort(temp,temp+2*n+2);
Count = unique(temp,temp+2*n+2)-temp;
for(int i = 0;i <= n;i++) {
int index = lower_bound(temp,temp+Count,s[i])-temp;
Rank[i] = index+1;
}
ll res = 0;
for(int i = n;i >=0;i--) {
ll curRank = lower_bound(temp,temp+Count,s[i]+m)-temp;
curRank += 1;
res += Sum(curRank-1);
curRank = lower_bound(temp,temp+Count,s[i])-temp;
curRank += 1;
Add(curRank);
}
printf("%lld\n",res);
return 0;
}