进阶分块-分块二分详解

分块二分:

众所周知,分块可以实现一些奇奇怪怪的区间的问题,比如以下这个题:
给定一个 N N N N N N 个整数,分别为 A 1 , A 2 , A 3 , A 4 . . . . A N A_1,A_2,A_3,A_4....A_N A1,A2,A3,A4....AN 每次询问给出三个整数 l , r , k l,r,k l,r,k 求所有满足 l ≤ i ≤ r , a i ≤ k l \leq i \leq r,a_i \leq k lir,aik 的个数。
不难想到暴力,可以做到每次操作 N log ⁡ 2 N N \log_2 N Nlog2N 总计 M N log ⁡ 2 N MN \log_2 N MNlog2N 但这样的复杂度在 N , M ≤ 100000 N,M \leq 100000 N,M100000 时是不容易过掉的。因此考虑优化它。
如果用分块该如何实现呢?
可以把每一个块内的元素排序,然后整块时直接二分答案,散块暴力处理,这样就能将时间复杂度降至 O ( Q N log ⁡ 2 N ) O(Q\sqrt N \log_2 N) O(QN log2N) 但这样是不容易过的,因此继续考虑优化它,不难想到,当整块时,块长越大, log ⁡ 2 ( r − l + 1 ) \log_2 (r-l+1) log2(rl+1) 是基本不变的,但是可以减少块的数量,因此我们可以将块长设为 N / N log ⁡ N N/\sqrt{N\log N} N/NlogN 这样做复杂度可以近似 O ( N Q log ⁡ 2 N ) O(N\sqrt{Q \log_2 N}) O(NQlog2N ) 可以通过此题。

样例:
block.in

5 3
1 2 3 4 5
1 5 3
1 4 3
2 5 2

block.out

3
3
1

代码:

//writer : tomxi  
#include<cstdio>
#include<algorithm>
#include<iostream>

using std::sort;
const int N=1e5+5;
const int K=300;
int a[N],d[N],n,q,l,r,left,right,lk,rk,belong[N],L[K],R[K],len,k;

inline int query(int lf,int rt,int val){
	lk=belong[lf],rk=belong[rt];
	int ans=0;
	if(lk==rk){
		for(int i=lf;i<=rt;i++) if(a[i]<=val) ans++;
		return ans;
	}
	for(int i=lf;i<L[lk+1];i++) if(a[i]<=val) ans++;
	for(int i=rt;i>R[rk-1];i--) if(a[i]<=val) ans++;
	for(int i=lk+1;i<=rk-1;i++){
		left=L[i],right=R[i];
		int res=0;
		while(left<=right){
			int mid=(left+right)>>1;
			if(d[mid]<=k){
				res=mid-L[i]+1;
				left=mid+1;
			}else{
				right=mid-1;
			}
		}
		ans+=res;
	}
	return ans;
}
int main(){
	scanf("%d%d",&n,&q);
	len=sqrt(n*25);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) d[i]=a[i];
	for(int i=1;i<=n;i++) belong[i]=(i-1)/len+1; 
	for(int i=1,j=n;i<=n;i++,j--){
		R[belong[i]]=i;L[belong[j]]=j;
	}	
	for(int i=belong[1];i<=belong[n];++i) sort(d+L[i],d+R[i]+1);
	while(q--){
		scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",query(l,r,k));
	}
	return 0;
}
  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值