分析
4316. 合适数对
题意:求区间和小于给定值
约束:值域很大,数据很稀疏,前缀和不具有单调性。
分析:当前缀和不具有单调性的时候,首先考虑使用树状数组。
// 1. 前缀和很大 1e14
// 2. 数据很稀疏
// 3. s[r] - s[l-1] < t ==> s[l-1] > s[r] - t;
// 4. 树状数组
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using LL = long long;
const int N = 4e5+10;
vector<LL> alls;
LL s[N];
int tr[N];
int a[N];
int main(){
LL n, t;
cin >>n>>t;
// 1. 前缀和很大 1e14
// 2. 数据很稀疏
// 3. s[r] - s[l-1] < t ==> s[l-1] > s[r] - t;
// 4. 树状数组
// 首先会用到的前缀和是s[0]到s[n] 同时 每个位置-t
memset(s, 0, sizeof s);
alls.push_back(0);
int x;
for(int i = 1; i <= n; i ++ ){
cin >> x;
s[i] += s[i - 1] + x;
alls.push_back(s[i]);
}
// 查询的坐标
for(int i = 0; i <= n; i ++) {
alls.push_back(s[i] - t);
}
// 离散化
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
// 树状数组
auto add = [&](int x, int c) {
for(int i = x; i < N; i += (i&-i)) {
tr[i] += c;
}
};
auto get = [&](LL x) { // 将大的数值转换为下标
int l = 0, r = alls.size() - 1;
while(l < r) {
int mid = l + r >> 1;
if(alls[mid] >= x) r = mid;
else l = mid + 1;
}
return l + 1;
};
auto query = [&](int u) {
int r = 0;
for(int i = u; i; i -= (i&-i)){
r += tr[i];
}
return r;
};
// 树状数组 首先应该添加的元素
add(get(0), 1);
LL res = 0;
for(int i = 1; i <= n; i++) {
// 求x > cur 求比当前值大的方式 1. query(n) - query(cur)
res += query(alls.size()) - query(get(s[i] - t));
add(get(s[i]), 1);
}
cout<<res<<endl;
return 0;
}