数据结构之分块

时间复杂度: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 
	}
}

 

二、分块进阶

1)分块入门9(Loj#6285)

 

题目描述

给出一个长为 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<

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(分块,数据结构,数据结构,分块,优秀)