牛客周赛 Round 28 F

本文介绍了如何在处理可能存在负数的区间前缀和问题时,利用树状数组的数据结构来实现高效查找合法位置。作者对比了map和set,强调了树状数组在支持二分查询和下标访问方面的优势。
摘要由CSDN通过智能技术生成

在这里插入图片描述
以后需要使用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;
}
  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值