以后需要使用map,set进行二分,并且需要知道二分位置的信息时,不妨考虑使用树状数组进行维护
因为简单版本保证了每个数都为正整数,所以前缀和保证了一定的递增的,即有序的,那么考虑固定左端点,去枚举右端点,用二分去找到第一个合法的位置,那么从该位置到数组结尾,一直为合法的,或者使用双指针进行维护也行。
基于简单版本的思想,那么对于区间问题,我们同样考虑去固定一个端点,去维护另外一个,又因为 a i a_i ai可能为负数,所以前缀和不保证单调性了,不能采用二分的方法,此时想到,我们对于每个右端点,我去计算其对应左端点的贡献即可,那么我每遍历一个位置,我就把该位置的前缀和放入一个数据结构中,该结构必须保证有序,这样对于当前位置,我一样可以使用二分该数据结构,然后找到合法的位置,一开始在考虑map,set之类,但这种虽说支持二分,但是不支持下标访问,即我无法知道对于合法位置之前有多少个数,这时候就该树状数组登场了,因为这题数据范围过大,所以需要先进行离散化,树状数组中插入该前缀和所在的位置即可,树状数组的查询同样是log级别,至此此题结束。
#include <bits/stdc++.h>using namespace std;
const int N = 5e5+ 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"struct MIT
{
ll tr[N];
int lowbit(int x) {return x & (-x);
}void add(int u, int v) {for (int i = u; i < N; i += lowbit(i)) {tr[i] += v;}
}ll query(int x) {ll res = 0;for (int i = x; i > 0; i -= lowbit(i)) {res += tr[i];}return res;
}
};MIT tr;void solve()
{ll n,k;cin>>n>>k;vector<ll> a(n+5),s(n+5);for(int i=1;i<=n;i++) cin>>a[i];vector<ll> p;for(int i=1;i<=n;i++) {s[i]=s[i-1]+a[i];p.push_back(s[i]);p.push_back(s[i]-k);}p.push_back(0);ll ans=0;sort(p.begin(),p.end());p.erase(unique(p.begin(),p.end()),p.end());int t=lower_bound(p.begin(),p.end(),0)-p.begin()+1;tr.add(t,1);for(int i=1;i<=n;i++){ll tar=s[i];int t=lower_bound(p.begin(),p.end(),tar)-p.begin()+1;tr.add(t,1);int pos=lower_bound(p.begin(),p.end(),s[i]-k)-p.begin()+1;
// cout<<tr.query(pos)<<endl;ans+=tr.query(pos);}cout<<ans<<endl;} int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t;t=1;// cin>>t;while(t--){solve();}system("pause");return 0;
}