分块的思想其实就是空间换时间,对于完整区间在查询时可简便查询,而对于不完整的区域,直接暴力查找即可。故查询时间最多就是2*(block)+(完整区间的数量);而总的时间只需要加上预处理的时间即可。所以做题时这个总的时间就需要再权衡一下,如果题目中查询次数较多可以考虑将区间大小适当的减小,否则就设成sqrt(n)。
重点在于找到合适的存储方法。
1.只涉及区间加法 传送门
对于这个只有区间加法,对于完整的块,可以增设一个数组只用来存完整区间还需加的数,类似于线段树的lazy标记,所以对于一个[L,R],对其中的完整区间进行lazy标记,对于不完整的区间对直接对a[i]进行修改,同时对于一个块的数都存进vector里面,并排序,这样在查询完整区间时,可以用二分查找,同时查询时对于每一个都是C*C-lazy[当前块的编号].
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int maxn = 1e5+10;
int a[maxn],pos[maxn];
int tag[maxn],n,block,sum;
vector<int>v[maxn];
void init(){
block=sqrt(n);
sum=n/block;
if(n%block)
sum++;
for(int i = 1; i <= n ; i++){
pos[i]=(i-1)/block+1;
v[pos[i]].push_back(a[i]);
}
for(int i = 1; i <= sum ; i++){
sort(v[i].begin(),v[i].end());
}
}
void resort(int po){/对于不完整区间重新排序
v[pos[po]].clear();
for(int i = (pos[po]-1)*block+1; i <= min(pos[po]*block,n); i++){
v[pos[po]].push_back(a[i]);
}sort(v[pos[po]].begin(),v[pos[po]].end());
}
void update(int l,int r,int x){
for(int i = l ; i <= min(pos[l]*block,r); i++){
a[i]+=x;
}resort(l);
if(pos[l]!=pos[r]){
for(int i = (pos[r]-1)*block+1; i <= r ; i++)
a[i]+=x;
resort(r);
}
for(int i = pos[l]+1; i <= pos[r]-1; i ++)
tag[i]+=x;
}
int query(int l,int r,int x){
int res=0;
for(int i = l ; i <= min(pos[l]*block,r); i++)
if(a[i]+tag[pos[i]] < x)
res++;
if(pos[l]!=pos[r]){
for(int i = (pos[r]-1)*block+1 ; i <= r ; i++)
if(a[i]+tag[pos[i]] < x)
res++;
}
for(int i = pos[l]+1 ; i <= pos[r]-1; i ++){
int tem=x-tag[i];
res+=lower_bound(v[i].begin(),v[i].end(),tem)-v[i].begin();
}return res;
}
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n ; i++){
scanf("%d",&a[i]);
}
init();
for(int i = 0 ; i < n ; i++){
int po,l,r,c;
scanf("%d%d%d%d",&po,&l,&r,&c);{
if(!po){
update(l,r,c);
}
else if(po == 1){
printf("%d\n",query(l,r,c*c));
}
}
}
return 0;
}
2.区间最小众数 传送门
对于一段[L,R]最小众数只可能出现在完整区间内或是非完整区间内,先把数都离散化,然后把相同的数的下标都存到一个vector里面,这时vector里面的是升序的。然后用一个f[i][j]数组表示从i号块到j号块的最小众数。所以查询时,对于每一可能的最大值我们都可以在其vector里面查询到在[L,R]里面出现的次数
设成block=sqrt(n)的时候超时了,此时查询次数为n比较大,所以把块设得小一点,设成200就可以了!!!
#include <iostream>
#include <map>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#define fkl(x) ((x-1)*bk+1)
#define fkr(x) (x*bk)
using namespace std;
const int maxn = 1e5+10;
vector<int>v[maxn];
map<int,int>m;
int pos[maxn],vis[maxn];
int a[maxn],n,bk,cut[maxn],f[505][505],F[505][505];
void init(){
for(int i = 1; i <= n ; i++){
scanf("%d",&a[i]);
}
bk=200;
for(int i = 1; i <= n ; i++){
m[a[i]]=1;
pos[i]=(i-1)/bk+1;
}
int k=1;
for(map<int,int>::iterator it=m.begin();it!=m.end();it++){
vis[k]=it->first;
m[it->first]=k++;
}
for(int i = 1 ; i <= n ; i++){
a[i]=m[a[i]];
v[a[i]].push_back(i);
}
}
void pre(int x){
memset(cut,0,sizeof cut);
int Max=0,po;
for(int i = fkl(x) ; i <= n ; i++){
int t=++cut[a[i]];
if(t>Max||(t==Max&&a[i]<po)){
Max=t,po=a[i];
}
f[x][pos[i]]=po;
}
}
void build(){
for(int i = 1; i <= pos[n] ; i++){
pre(i);
}
}
int research(int l,int r,int x){/查询在[l,r]内x出现了几次
return upper_bound(v[x].begin(),v[x].end(),r)-lower_bound(v[x].begin(),v[x].end(),l);
}
int query(int l,int r){
int po=f[pos[l]+1][pos[r]-1];
int Max=research(l,r,po);
for(int i = l ; i <= min(fkr(pos[l]),r) ; i++){
int t=research(l,r,a[i]);
if(t>Max||(t==Max&&a[i]<po)){
Max=t,po=a[i];
}
}
if(pos[l]!=pos[r]){
for(int i = fkl(pos[r]); i <= r; i ++){
int t=research(l,r,a[i]);
if(t>Max||(t==Max&&a[i]<po)){
Max=t,po=a[i];
}
}
}
return po;
}
int main()
{
scanf("%d",&n);
init();
build();
for(int i = 1; i <= n ; i++){
int l,r;scanf("%d%d",&l,&r);
printf("%d\n",vis[query(l,r)]);
}
return 0;
}