分块打表
这是什么?
很多时候,我们会遇到这种题:
给出一个区间 \([L,R]\),求出该区间内满足 某条件 的数的个数,\(0 \le L,R \le 2\times10^9\)。
显然,暴力是过不去的,而如果我们打表打出前缀和数组,则会因代码过长而交不上去;
所以,我们可以将这两者的优点结合起来,这就是——分块打表。
怎么做?
以 \(T\) 为块长,预处理出一个 \(pre\) 数组,\(pre_i\) 表示到(即前缀和)第 \(i\) 块的答案数量;
那么如果 \(L\) 和 \(R\) 在同一块里,直接暴力;否则整块取出预处理好的的答案,两边零散块暴力。
复杂度 \(O(T \ \cdot n)\),\(n\) 为单次判断的复杂度。
显然块长越小越好,但减小块长的代价是增加码长,所以应选出一个合适的块长。
一般来讲,对于 \(2\times10^9\) 的值域,块长取 \(5\times10^5\) 是一个不错的选择。
打表代码:
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=2e9;
int check(int x){
/*这里是对一个数是否符合要求的判断*/
}
int cnt,ans;
main(){
cout<<"int pre[]={0";
int len=5e5;//len 为块长,可以找一个最适合自己的
for(int i=1;i<=N;i++){
ans+=check(i);
if(++cnt==len){
cout<<","<<ans;
cnt=0;
}
}
cout<<"};";
return 0;
}
提交代码:
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
int l,r;
int pre[]={/*这里是你打好的表*/};
int check(int x){
/*这里是对一个数是否符合要求的判断*/
}
int getans(int l,int r){//计算答案
if(l>r)return 0;
int ans=0;
int L=(l/500000),R=(r/500000);
if(L==R){
for(int i=l;i<=r;i++)ans+=check(i);
return ans;
}
ans=pre[R]-pre[L];
for(int i=L*500000+1;i<=l-1;i++)ans-=check(i);
for(int i=R*500000+1;i<=r;i++)ans+=check(i);
return ans;
}
main(){
cin>>l>>r;
cout<<getans(l,r);
return 0;
}
浙公网安备 33010602011771号