Codeforces 1042D weighted line segment tree + thinking transformation

The meaning of the question: Given n numbers and a number t, ask you how many situations can make, al +a(l+1)+…a(r-1)+ar <t ,(l<=r), That is, how many ways to find the two-tuple (l, r)?

Idea: We can think of using prefix sum to solve the interval sum from l to r, but it will definitely not work to enumerate l and r violently, so we can convert the target condition we are looking for: sum[R]-sum [L-1] <t, can be obtained by shifting the terms on both sides, that is, sum[L-1]> sum[R]-t, after we use sum[R]-t as the standard, the question becomes how many If the sum[L] is larger than this number, it can become a problem similar to finding the inverse ordinal number. You can use the line segment tree of the complete set to quickly find the size of this number.

The value range of the a array is relatively large, so the obtained prefix and sum and sum-t can be discretized to build a tree.

Can be used as a solution to this type of problem. The weighted line segment tree can be helpful for finding the problem of greater or lesser than a certain value that exists in the entire interval.

AC code:

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 2e5+7;
typedef long long ll;
ll sum[MAXN],a[MAXN],b[MAXN<<1],tree[MAXN<<4];
//这里的数组 要开8倍大小 因为sum和sum-t两个不同的值

void build(int rt,int l,int r)
{
    
    
	if(l == r){
    
    
		tree[rt] = 0;
		return ;
	}
	int mid = (r+l)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}

void update(int rt,int l,int r,int p,int val)
{
    
    
	if(l  == r){
    
    
		tree[rt] += val;
		return ;
	}
	int mid = (l+r)>>1;
	if(p <= mid) update(rt<<1,l,mid,p,val);
	else update(rt<<1|1,mid+1,r,p,val);
	tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}

int query(int rt,int l,int r,int ql,int qr)//区间内的数出现的次数
{
    
    
	if(l >= ql && r <= qr){
    
    
		return tree[rt];
	}
	int ans = 0;
	int mid = (l+r)>>1;
	if(ql <= mid) ans += query(rt<<1,l,mid,ql,qr);
	if(qr > mid) ans += query(rt<<1|1,mid+1,r,ql,qr);
	return ans;
}

int main()
{
    
    
	int n;ll t;
	scanf("%d%lld",&n,&t);
	int cnt = 0;
	b[++cnt] = 0;
	for(int i = 1;i <= n;i ++){
    
    
		scanf("%lld",&a[i]);
		sum[i] = sum[i-1] + a[i];
		b[++cnt] = sum[i];
		b[++cnt] = sum[i]-t;	
	}
	sort(b+1,b+1+cnt);
	int num = unique(b+1,b+1+cnt)-b-1;
	build(1,1,num);
	ll ans = 0;
	// 将前缀和变成元素 从而转化成 类似于求逆序数的过程
	update(1,1,num,(lower_bound(b+1,b+1+num,0)-b),1);
	for(int i = 1;i <= n;i ++){
    
    
		int pos = lower_bound(b+1,b+1+num,(sum[i]-t))-b;
		ans += query(1,1,num,pos+1,num);//第一种 直接找比它大的
		//(ans += i - query(1,1,num,1,pos);
		//第二种 这里的i代表的是 下标 也就是从r往前开始枚举 找比它小的 因为他前面有l个前缀
		// 所以只需要找出 比他小的sum有哪些 那么前i个中 剩下的就是比它大的
		update(1,1,num,lower_bound(b+1,b+1+num,sum[i])-b,1);
	}
	printf("%lld\n",ans);
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_45672411/article/details/108409963