牛客周赛 Round 28 F

F.小红统计区间(hard)

题目链接

Sum[r]-Sum[l]\geq k\ \Rightarrow \ Sum[r]-k\geq Sum[l]\ \Rightarrow \ ans++

Sum[i]\geq k\Rightarrow ans++

Sum[i]为前缀和

  • 枚举右端点看有多少个左端点满足条件,即在一个数轴上找x\leq Sum[i]-kx的个数。可以利用树状数组区间查询,查找1\rightarrow i-1中满足条件的前缀和。具体操作为先查找,再把自身在数轴上对应的数的个数加一。所以统计时没有统计Sum[i]\geq k自身对答案的影响。当前操作为第i位时,则数轴上只记录了1\rightarrow i-1的前缀和。
  • 由于前缀和过大,形成的数轴过长,采用离散化。将所有前缀和由小到大排序并去重,构成新数轴。
  • 由于Sum[l]-k在数轴上可能没有直接映射,所以通过二分找到满足条件的最大的前缀和,转为统计小于等于它的前缀和个数。找不到就不更新ans
  • ans最大会到1e10,会爆int


import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;



public class Main{
	static 
	class BIT{
		int size;
		long[] tr;
		public BIT(int n) {
			size=n;
			tr=new long[n+1];
		}
		public int lowbit(int x) {
			return x&(-x);
		}
		public void update(int x) {
			for(int i=x;i<=size;i+=lowbit(i)) {
				tr[i]++;
			}
		}
		public long query(int x) {//区间组合最多1e10,int会爆
			long ans=0;
			for(int i=x;i>0;i-=lowbit(i)) {
				ans+=tr[i];
			}
			return ans;
		}
	}
	public static void main(String[] args) throws IOException{
		Scanner input=new Scanner(System.in);
		int n=input.nextInt();
		long k=input.nextLong();
		long ans=0;
		long[] Sum=new long[n];//用于离散化的前缀和
		long[] pre=new long[n];//保留原本的前缀和
		Sum[0]=input.nextLong();
        if(Sum[0]>=k)ans++;
		pre[0]=Sum[0];
		for(int i=1;i<n;++i) {
			Sum[i]=input.nextLong();
			Sum[i]+=Sum[i-1];
			if(Sum[i]>=k)ans++;
			pre[i]=Sum[i];
		}
		Arrays.sort(Sum);
		HashMap<Long,Integer> hs=new HashMap<Long, Integer>();
		int cnt=0;
		long[] Qu=new long[n+1];//离散后数轴每位对应的前缀和
		for(int i=0;i<n;++i) {
			if(hs.get(Sum[i])==null) {
				cnt++;
				hs.put(Sum[i], cnt);
				Qu[cnt]=Sum[i];
			}
		}
		//排序后位置不同,可能在后面的前缀和被移到前面或在前面的前缀和被移到后面
		
		BIT Tr=new BIT(cnt);
		Tr.update(hs.get(pre[0]));
		for(int i=1;i<n;++i) {
			long y=pre[i]-k;
			int l=1,r=cnt,id=-1;//二分查找
			while(l<=r) {
				int mid=(l+r)>>1;
				if(Qu[mid]<=y) { 
					id=mid;
					l=mid+1;
				}
				else r=mid-1;
			}
			if(id!=-1)ans+=Tr.query(hs.get(Qu[id]));
			Tr.update(hs.get(pre[i]));
		}
		System.out.println(ans);
	}
	
}

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值