时间复杂度:n√n
Q:和线段树等较复杂数据结构相比,它有什么优势?
A:首先,看到时间复杂度会让人联想到哪个算法?
Q:莫队。
A:是的,莫队!莫队号称能解决一切区间问题,然而它是离线的;但是我们的分块却是在线处理!!!
1)数据结构能接受的数据范围分块基本上能搞
2)分块代码短,容易调试
这两个优点不是秒掉主席树,树套树,splay……
hzwer入门8题:传送门
分块的大体模板没什么好讲的,但是注意的是可以学习另一种写法,因为黄学长的写法有时会不好处理:
inline void prep(){
……
siz=sqrt(n);
FOR(i,1,n)bl[i]=(i-1)/siz+1;
……
}
inline void Add_Query(int L,int r,int c){
if(bl[r]-bl[L]<=1){//两块相邻或在一个块中 √n
FOR(i,L,r)……; ……
}else {
FOR(i,L,bl[L]*siz)……; //散块√n
……FOR(i,bl[L]+1,bl[r]-1)……;//整体操作√n
FOR(i,(bl[r]-1)*siz+1,r)……;//处理√n
}
}
题目描述
给出一个长为 n 的数列,以及 n 个操作,操作涉及询问区间的最小众数。
输入格式
第一行输入一个数字 n。
第二行输入 n个数字,第 i 个数字为 ai,以空格隔开。
接下来输入 n 行询问,每行输入两个数字 l、r以空格隔开。
表示查询位于[l,r]的数字的众数。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例
样例输入
4
1 2 2 4
1 2
1 4
2 4
3 4
样例输出
1
2
2
2
发现不是强制在线,莫队乱搞~这里就顺便讲一波回滚莫队,大体思路不变,还是分区间:
1、我们以块编号为第一关键字排序,右端点位置为第二关键字排序
2、询问时依次枚举区间,我们保留右端点的移量(右边单增),左端点则每次在这一个块中来回移动
3、下一个块时,清空统计答案重做
所以对于每一个块:左端点每次操作√n,右端点总共移n,均摊√n,因此保证了n√n::~回滚莫队~
为什么分块你给我看莫队?离线嘛……强制在线的出题人都好无聊= =
这里给出蒲公英的分块代码(强制在线的区间众数)
#include
using namespace std;
const int N = 1e5,M = N+10,Maxsiz = 322;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
int n,m,siz,las,k[M],bl[M],fp[M];
int a[Maxsiz][Maxsiz],s[Maxsiz][M];//[i,j]区间的众数,前i个块m出现的次数
maprp;
inline void prep(int x){
int ans,num=0,idx,cnt[M];
memset(cnt,0,sizeof(cnt));
Inc(i,(x-1)*siz+1,n){
++cnt[fp[i]];
idx=(i-1)/siz+1;
if(cnt[fp[i]]>=num){
if(cnt[fp[i]]==num&&k[i]num)ans=k[i],num=cnt[fp[i]];
}
a[x][idx]=ans;
}
}
inline void init(){
scanf("%d%d",&n,&m);
Inc(i,1,n)scanf("%d",k+i);
siz=sqrt(n);
Inc(i,1,n)bl[i]=(i-1)/siz+1;
}
inline void disc(){
int tmp[M];
Inc(i,1,n)tmp[i]=k[i];
sort(tmp+1,tmp+1+n);
int len=unique(tmp+1,tmp+1+n)-tmp-1;
Inc(i,1,n)fp[i]=rp[k[i]]=lower_bound(tmp+1,tmp+1+len,k[i])-tmp;
Inc(i,1,n)++s[(i-1)/siz+1][fp[i]];
Inc(i,1,bl[n])Inc(j,1,n)s[i][j]+=s[i-1][j];//n^(3/2)
Inc(i,1,bl[n])prep(i);
}
int tmp[M];
inline int Query(int L,int r){
int cur_ans,cur_num=0;
if(bl[r]-bl[L]<=1){//暴力统计众数
Inc(i,L,r)tmp[fp[i]]=0;
Inc(i,L,r)++tmp[fp[i]];
Inc(i,L,r)if(tmp[fp[i]]>=cur_num){
if(tmp[fp[i]]==cur_num&&k[i]cur_num)cur_ans=k[i],cur_num=tmp[fp[i]];
}
Inc(i,L,r)--tmp[fp[i]];
}else {
cur_ans=a[bl[L]+1][bl[r]-1],cur_num=s[bl[r]-1][rp[cur_ans]]-s[bl[L]][rp[cur_ans]];
//统计
Inc(i,L,bl[L]*siz)tmp[fp[i]]=s[bl[r]-1][fp[i]]-s[bl[L]][fp[i]];
Inc(i,(bl[r]-1)*siz+1,r)tmp[fp[i]]=s[bl[r]-1][fp[i]]-s[bl[L]][fp[i]];
Inc(i,L,bl[L]*siz)++tmp[fp[i]];
Inc(i,(bl[r]-1)*siz+1,r)++tmp[fp[i]];
//撤销
Inc(i,L,bl[L]*siz){
if(tmp[fp[i]]==cur_num&&k[i]cur_num)cur_ans=k[i],cur_num=tmp[fp[i]];
}
Inc(i,(bl[r]-1)*siz+1,r){
if(tmp[fp[i]]==cur_num&&k[i]cur_num)cur_ans=k[i],cur_num=tmp[fp[i]];
}
}
return las=cur_ans;
}
inline void solv(){
int L,r;
while(m--){
scanf("%d%d",&L,&r);
L=(L+las-1)%n+1,r=(r+las-1)%n+1;
if(L>r)swap(L,r);
cout<