分块二分:
众所周知,分块可以实现一些奇奇怪怪的区间的问题,比如以下这个题:
给定一个
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
l≤i≤r,ai≤k 的个数。
不难想到暴力,可以做到每次操作
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,M≤100000 时是不容易过掉的。因此考虑优化它。
如果用分块该如何实现呢?
可以把每一个块内的元素排序,然后整块时直接二分答案,散块暴力处理,这样就能将时间复杂度降至
O
(
Q
N
log
2
N
)
O(Q\sqrt N \log_2 N)
O(QNlog2N) 但这样是不容易过的,因此继续考虑优化它,不难想到,当整块时,块长越大,
log
2
(
r
−
l
+
1
)
\log_2 (r-l+1)
log2(r−l+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;
}