分块

小小萌新刚开始学分块,还请求分块大人善待(^_-)

分块嘛,总觉得分块代码比SGT(线段树)短,比BIT(树状数组)好理解,但好多大佬都推荐BIT,算了,多学了总比少学好。。。

一号板子题:

先上一道基础题对分块的中心思想加以理解,,,

单点修改&单点查询

(也可以用线段树写)

#include<bits/stdc++.h>
#define LL long long 
using namespace std;
const int sea=1e5+7;
int n,q,x,y,block,num,s,belong[sea],l[sea],r[sea];
LL maxx[sea],a[sea];
void build()//建块
{
	block=sqrt(n); num=n/block; 
	if(n%block) num++;
	for(int i=1;i<=num;i++)
	l[i]=(i-1)*block+1,r[i]=i*block;
	r[num]=n;
	for(int i=1;i<=n;i++)
	belong[i]=(i-1)/block+1;
	//(建块板子)
	for(int i=1;i<=num;i++)
	for(int j=l[i];j<=r[i];j++)
	maxx[i]=max(maxx[i],a[j]);
}
void alter(int x,int y)//单点修改
{
	a[x]+=y;
	maxx[belong[x]]=max(maxx[belong[x]],a[x]);
}
LL ask(int x,int y)//单点查询
{
	LL ans=0;
	if(belong[x]==belong[y])
	{
		for(int i=x;i<=y;i++)
		ans=max(ans,a[i]);
		return ans;
	}
	for(int i=x;i<=r[belong[x]];i++)
	ans=max(ans,a[i]);
	for(int i=belong[x]+1;i<belong[y];i++)
	ans=max(ans,maxx[i]);
	for(int i=l[belong[y]];i<=y;i++)
	ans=max(ans,a[i]);
	return ans;
}
int main()
{
	scanf("%d%d",&n,&q); 
	memset(a,0,sizeof(a));
	memset(maxx,0,sizeof(maxx));
	build();
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d",&s,&x,&y);
		if(s==1) alter(x,y);
		else printf("%lld\n",ask(x,y));
	}	
	return 0;
}
既然下定决心学习分块就少不了hzwer学长的:数列分块入门

(坚持刷完!!!!)

二号板子题:
区间修改&单点查询

(还是推荐LOJ,比较好的一个OJ)
这是一系列的分块练习,认为还是比较全的。

#include<bits/stdc++.h>
#define LL long long 
using namespace std;
const int sea=1e5+7;
int n,block,belong[sea],num,l[sea],r[sea],Lazy[sea],x,y,z,s;
LL a[sea];
void build()
{
	block=sqrt(n); num=n/block;
	if(n%block) num++;
	for(int i=1;i<=num;i++)
	l[i]=(i-1)*block+1,r[i]=i*block;
	r[num]=n;
	for(int i=1;i<=n;i++)
	belong[i]=(i-1)/block+1;
}
void alter(int x,int y,int z)
{
	if(belong[x]==belong[y])
	for(int i=x;i<=y;i++) a[i]+=z;
	else
	{
		for(int i=x;i<=r[belong[x]];i++) a[i]+=z;
		for(int i=belong[x]+1;i<belong[y];i++) Lazy[i]+=z; //最最最重要的分块代码,傻掉的我竟然写错了,55555,所以菜鸡就是菜鸡~~~
		for(int i=l[belong[y]];i<=y;i++) a[i]+=z;
	}
}
LL ask(int x)
{
	return a[x]+Lazy[belong[x]]; 
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	build();
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&s);
		if(s==0)
		{
			scanf("%d%d%d",&x,&y,&z);
			alter(x,y,z);
		}
		else
		{
			scanf("%d%d%d",&x,&y,&z);
			printf("%d\n",ask(y));
		}
	}
	return 0;
}
三号板子题:
区间修改&区间查询

看讨论组里有大佬说这个题暴力O3都能过,数据怎么可以这么水,但是还是要好好练习分块。。。

#include<bits/stdc++.h>
#define LL long long 
using namespace std;
const int sea=1e5+7;
int n,block,belong[sea],num,l[sea],r[sea],Lazy[sea],x,y,z,s;
LL a[sea];
vector<int>v[sea];//想了很久原来想用sort,然后想用堆优化,看了看大佬们的题解,都是用verctor也就这样用了,堆应该也能写
void reset(int x)//对第x块的处理
{
	v[x].clear();
	for(int i=(x-1)*block+1;i<=min(x*block,n);i++)
	v[x].push_back(a[i]);
	sort(v[x].begin(),v[x].end());
}
void alter_ad(int x,int y,LL z)//区间修改
{
	for(int i=x;i<=min(belong[x]*block,y);i++) a[i]+=z;
	reset(belong[x]);
	if(belong[x]!=belong[y])
	{
		for(int i=block*(belong[y]-1)+1;i<=y;i++) a[i]+=z;
		reset(belong[y]);
	}
	for(int i=belong[x]+1;i<belong[y];i++) Lazy[i]+=z;
}
LL ask(int x,int y,LL z)//区间查询
{
	LL ans=0;
	for(int i=x;i<=min(belong[x]*block,y);i++) 
	if(a[i]+Lazy[belong[x]]<z) ans++;
	if(belong[x]!=belong[y])	
	{
		for(int i=belong[x]+1;i<belong[y];i++) 
		{
			LL xx=z-Lazy[i];
			ans+=lower_bound(v[i].begin(),v[i].end(),xx)-v[i].begin();
		}
		for(int i=(belong[y]-1)*block+1;i<=y;i++)
		if(a[i]+Lazy[belong[y]]<z) ans++;
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	block=0.5*sqrt(n);
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&a[i]);
		belong[i]=(i-1)/block+1;
		v[belong[i]].push_back(a[i]);
	}
	for(int i=1;i<=belong[n];i++) 
    sort(v[i].begin(),v[i].end());
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&s);
		if(s==0)
		{
			scanf("%d%d%d",&x,&y,&z);
			alter_ad(x,y,z);
		}
		else
		{
			scanf("%d%d%d",&x,&y,&z);
			printf("%lld\n",ask(x,y,z*z));
		}
	}
	return 0;
}
四号板子题:
区间修改&区间查询(前驱)

当时一看到这道题,还不知道前驱是什么,就找了找hzwer学长的博客,再次强调一下:
前驱:是指在此数之前的小于此数最大的数值
(解法与之前第三题基本一样)

#include<bits/stdc++.h>
#define LL long long 
using namespace std;
const int sea=1e5+7;
int n,block,belong[sea],num,l[sea],r[sea],Lazy[sea],x,y,z,s;
int a[sea];
vector<int>v[sea];//vector对于排序用的比较方便,但是我个人还是比较喜欢不用它,毕竟STL不是很快
void build()//还是习惯于建块(可能是因为线段树吧)
{
	block=sqrt(n);
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&a[i]);
		belong[i]=(i-1)/block+1;
		v[belong[i]].push_back(a[i]);
	}
	for(int i=1;i<=belong[n];i++) 
    sort(v[i].begin(),v[i].end());
    //基本建块+vector的操作
}
void reset(int x)
{
	v[x].clear();
	for(int i=(x-1)*block+1;i<=min(x*block,n);i++)
	v[x].push_back(a[i]);
	sort(v[x].begin(),v[x].end());
}
void alter_ad(int x,int y,LL z)
{
	for(int i=x;i<=min(belong[x]*block,y);i++) a[i]+=z;
	reset(belong[x]);
	if(belong[x]!=belong[y])
	{
		for(int i=block*(belong[y]-1)+1;i<=y;i++) a[i]+=z;
		reset(belong[y]);
	}
	for(int i=belong[x]+1;i<belong[y];i++) Lazy[i]+=z;
}
int ask(int x,int y,LL z)
{
//基础操作和第二题基本相同,就是改一下求前驱即可
	int ans=-1;
	for(int i=x;i<=min(belong[x]*block,y);i++) 
	if(a[i]+Lazy[belong[x]]<z) 	ans=max(ans,a[i]+Lazy[belong[x]]);
	if(belong[x]!=belong[y])	
	{
		for(int i=(belong[y]-1)*block+1;i<=y;i++)
		if(a[i]+Lazy[belong[y]]<z) 
		ans=max(ans,a[i]+Lazy[belong[y]]);
	}
	for(int i=belong[x]+1;i<belong[y];i++) 
	{
		int kk=lower_bound(v[i].begin(),v[i].end(),z-Lazy[i])-v[i].begin();
		if(kk==0) continue;
		ans=max(ans,v[i][kk-1]+Lazy[i]);
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	build();
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&s);
		if(s==0)
		{
			scanf("%d%d%d",&x,&y,&z);
			alter_ad(x,y,z);
		}
		else
		{
			scanf("%d%d%d",&x,&y,&z);
			printf("%d\n",ask(x,y,z));
		}
	}
	return 0;
}
五号板子题:
区间修改&区间查询(求和取模)

啊啊啊啊啊啊~~~~这么简单的一道题耗费了我一个多小时,二十分钟打出来,用了八九十分钟去调,结果死在了手残上,打算剁手了555555555

#include<bits/stdc++.h>
#define sea 50005
#define LL long long 
using namespace std;
int block,num,belong[sea],l[sea],r[sea],n,s,x,y,z;
LL ans=0,a[sea],ins[sea],sum[sea];
void build()
{
	block=sqrt(n);	num=n/block;
	if(n%block) num++;
	for(int i=1;i<=num;i++)
	l[i]=(i-1)*block+1,r[i]=i*block;
	r[num]=n;
	for(int i=1;i<=n;i++)
	belong[i]=(i-1)/block+1,sum[belong[i]]+=a[i];
}
void alter_add(int x,int y,int z)
{
	if(belong[x]==belong[y])
	for(int i=x;i<=y;i++) a[i]+=z,sum[ins[i]]+=z;
	else
	{
		for(int i=x;i<=r[belong[x]];i++) a[i]+=z,sum[ins[i]]+=z;
		for(int i=belong[x]+1;i<belong[y];i++) ins[i]+=z;
		for(int i=l[belong[y]];i<=y;i++) a[i]+=z,sum[ins[i]]+=z;
	}
}
LL ask(int x,int y,int z)
{
	LL ans=0;
	if(belong[x]==belong[y])
		for(int i=x;i<=y;i++) ans+=a[i]+ins[belong[i]];
	else
	{
		for(int i=x;i<=r[belong[x]];i++) ans+=a[i]+ins[belong[x]];
		for(int i=belong[x]+1;i<belong[y];i++)  ans+=sum[i]+block*ins[i];
		for(int i=l[belong[y]];i<=y;i++) ans+=a[i]+ins[belong[y]];
	}
 	return ans;
}//还是喜欢这种风格(不用verctor),嘻嘻
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build();
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&s,&x,&y,&z);
		ans=0;
		if(s==0) alter_add(x,y,z);
		else printf("%lld\n",ask(x,y,z)%(z+1));
	}
	return 0;
}
六号板子题:
区间修改&区间查询(开方求和)

比较不好操作的区间修改,刚开始想着直接暴力打吧,但是想了想分块,开方在sum[]数组中不好操作,但是看了hzwer的博客之后,简直是大写的佩服,暴力开方还能写的这么强,重要的是还有优化了开方次数,太强了(膜的是不是有点过0.0)

#include<bits/stdc++.h>
#define sea 50005
#define LL long long 
using namespace std;
int block,num,belong[sea],l[sea],r[sea],n,s,x,y,z;
LL ans=0,a[sea],ins[sea],sum[sea];
void build()
{
	block=sqrt(n);	num=n/block;
	if(n%block) num++;
	for(int i=1;i<=num;i++)
	l[i]=(i-1)*block+1,r[i]=i*block;
	r[num]=n;
	for(int i=1;i<=n;i++)
	belong[i]=(i-1)/block+1,sum[belong[i]]+=a[i];
}
void kfbl(int x)//分块暴力(缩写)
{
	if(ins[x]) return;
	ins[x]=1;//标记数组
	sum[x]=0;//整块赋值为0
	for(int i=l[x];i<=r[x];i++)
	{
		a[i]=sqrt(a[i]),sum[x]+=a[i];//对于整块的分解,无限开方,但是如果有1或者0就记录下来每次跳过即可
		if(a[i]>1) ins[x]=0;
	}
}
void alter_kf(int x,int y)
{
	if(belong[x]==belong[y])
	for(int i=x;i<=y;i++) sum[belong[x]]-=a[i],a[i]=sqrt(a[i]),sum[belong[x]]+=a[i];//要记得先删除在修改最后添加
	else
	{
		for(int i=x;i<=r[belong[x]];i++) sum[belong[x]]-=a[i],a[i]=sqrt(a[i]),sum[belong[x]]+=a[i];
		for(int i=belong[x]+1;i<belong[y];i++) kfbl(i);
		for(int i=l[belong[y]];i<=y;i++) sum[belong[y]]-=a[i],a[i]=sqrt(a[i]),sum[belong[y]]+=a[i];
	}
}
LL ask(int x,int y)
{
	LL ans=0;
	if(belong[x]==belong[y])
	for(int i=x;i<=y;i++) ans+=a[i];
	else
	{
		for(int i=x;i<=r[belong[x]];i++) ans+=a[i];
		for(int i=belong[x]+1;i<belong[y];i++)  ans+=sum[i];
		for(int i=l[belong[y]];i<=y;i++) ans+=a[i];
	}
 	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build();
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&s,&x,&y,&z);
		ans=0;
		if(s==0) alter_kf(x,y);
		else printf("%lld\n",ask(x,y));
	}
	return 0;
}
七号板子题:
单点修改&单点查询(插入)

先介绍一下STL迭代器一个比较有用的工具(可我这个蒟蒻就只学会了用迭代器,,,0.0)
上代码:

//迭代器板子 
#include<bits/stdvc++.h>
using namespace std;
int main()
{
	int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	list<int> name(a,a+10);
	list<int>::iterator it;
	for (i = name.begin(); i != name.end(); i++)
	cout << *i << ' ';
	return 0;
}

也就是一道不错的练习线性分块的题,中心思想就是用好STL(主要是插入不会手打>.<)可以用vector也可以用list,(主要是不是很会要不就写了,,,)题解就是STL插入+重新建块优化(具体题解看注释)

#include<bits/stdc++.h>
#define sea 100005
#define LL long long  
using namespace std;
int num,block,a[sea],s,n,x,y,z;
vector<int>belong[1050];
int st[200005],xn,r;
inline int read()
{
	int s=0,w=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
void build()
{
	block=sqrt(n);
	for(int i=1;i<=n;i++)
	belong[(i-1)/block+1].push_back(a[i]);
	num=(n-1)/block+1;
}
pair<int,int> ins(int tt)
{
	int a=1;
	while(tt>belong[a].size())
	tt-=belong[a].size(),a++;
	return make_pair(a,tt-1);//记录其所在块序号x和在这块的tt-1处 
}
void rebuild()
{
	xn=0;
	for(int i=1;i<=num;i++)
	{
    	for(vector<int>::iterator j=belong[i].begin();j!=belong[i].end();j++)
    	//STL迭代器 
		st[++xn]=*j;
		belong[i].clear();
	}//清空块以及记录新块信息 
	int xblock=sqrt(xn);
	for(int i=1;i<=xn;i++)
	belong[(i-1)/xblock+1].push_back(st[i]);
	num=(xn-1)/xblock+1;//重新分块 
}
void alter_insert(int x,int y)
{
	pair<int,int> t=ins(x);//插入 
	belong[t.first].insert(belong[t.first].begin()+t.second,y);
	//遍历所有并处理
	//中心思想就是用STL直接插入然后优化,复杂度在O(n)范围内 
	if(belong[t.first].size()>20*block)//特判重新分块 
	rebuild();
}
LL ask(int r)
{
	pair<int,int> xx=ins(r);
	return belong[xx.first][xx.second];
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	build();
	for(int i=1;i<=n;i++)
	{
		s=read(); x=read(); y=read(); z=read(); 
		if(s==0) alter_insert(x,y);
		else printf("%lld\n",ask(y));
	}
	return 0;
} 
八号板子题:
区间修改&单点查询(乘法)

(对于蒟蒻的我来说,越往后做越难了。。。)
区间加乘的修改,在线段树板子的时候也写过,但是分块就不是很好写,还是按照分块的中心思想即可,写的时候记得加上个懒标记就可以了,,,

#include<bits/stdc++.h>
#define sea 100005
#define stream 1005
#define LL long long
#define mod  10007
using namespace std;
int n,block,num,l[sea],belong[sea],r[sea],x,y,z,s;
int a[sea],ad[stream],mu[stream];//(很像是线段树里的懒标记)
inline LL read()
{
	LL s=0,w=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1; ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
void build()
{
	block=sqrt(n); num=n/block;
	if(n%block) num++;
	for(int i=1;i<=num;i++)
	l[i]=(i-1)*block+1,r[i]=i*block; r[num]=n;
	for(int i=1;i<=n;i++)
	belong[i]=(i-1)/block+1;
}
void ins(int t)
{
	for(int i=l[t];i<=min(r[t],n);i++)
	a[i]=(a[i]*mu[t]+ad[t])%mod;
	ad[t]=0; mu[t]=1;
	//相当于线段树中的pushdown 
}
void alter(int s,int x,int y,int z)
{
	ins(belong[x]);
	for(int i=x;i<=min(r[belong[x]],y);i++)
	{
		if(s==0) a[i]+=z; 
		else a[i]*=z; 
		a[i]%=mod;
	}//前半块 
	if(belong[x]!=belong[y])
	{
		ins(belong[y]);
		for(int i=l[belong[y]];i<=y;i++)
		{
			if(s==0) a[i]+=z;
			else a[i]*=z;
			a[i]%=mod;
		}
	}//后半块 
	for(int i=belong[x]+1;i<belong[y];i++)
	{
		if(s==0) ad[i]=(ad[i]+z)%mod;
		else 
		{
			ad[i]=(ad[i]*z)%mod;
			mu[i]=(mu[i]*z)%mod;
		}
	}//整块 
}
LL ask(int tt)
{
	return a[tt]*mu[belong[tt]]+ad[belong[tt]];
}
int main()
{
	n=read(); block=sqrt(n);
	for(int i=1;i<=n;i++) a[i]=read(); 
	build();
	for(int i=1;i<=num;i++) mu[i]=1;  //乘法预处理       
	for(int i=1;i<=n;i++)
	{
		s=read(); x=read(); y=read(); z=read(); 
		if(s!=2)  alter(s,x,y,z);
		else printf("%lld\n",ask(y)%mod);
	}	
	return 0;
} 
九号板子题:
区间修改&区间查询(在线)
#include<bits/stdc++.h>
#define sea 100005
#define LL long long
using namespace std;
int n,block,num,l[sea],belong[sea],r[sea],x,y,z,a[sea],ins[sea];
inline LL read()
{
	LL s=0,w=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1; ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
void build()
{
	block=sqrt(n); num=n/block;
	if(n%block) num++;
	for(int i=1;i<=num;i++)
	l[i]=(i-1)*block+1,r[i]=i*block; r[num]=n;
	for(int i=1;i<=n;i++)
	belong[i]=(i-1)/block+1;
}
void dye(int t)
{
	if(ins[t]==-1) return ;
	for(int i=l[t];i<=r[t];i++) 
	a[i]=ins[t];
	ins[t]=-1;//染色 
}	
LL work(int x,int y,int z)
{
	LL ans=0;
	dye(belong[x]);
	for(int i=x;i<=min(r[belong[x]],y);i++)
	if(a[i]!=z) a[i]=z;
	else ans++; 
	//前半块 (暴力染色) 
	if(belong[x]!=belong[y])
	{
		dye(belong[y]);
		for(int i=l[belong[y]];i<=y;i++)
		if(a[i]!=z) a[i]=z;
		else ans++;
	} 
    //后半块 (暴力染色) 
	for(int i=belong[x]+1;i<belong[y];i++) 
		if(ins[i]!=-1)
		{
			if(ins[i]!=z) ins[i]=z;
			else ans+=block; //优化染色 		
		}
		else
		{
			for(int j=l[i];j<=r[i];j++)//重要的是要看好l[]r[]有傻掉的我写成了l[belong[i]],r[belong[i]];
			if(a[j]!=z) a[j]=z;
			else ans++;//没有染上色的整块就暴力染色 
			ins[i]=z;//染色剂 
		}
	//整块 
	return ans;
}
int main()
{
	memset(ins,-1,sizeof(ins));
	n=read(); build();
	for(int i=1;i<=n;i++) a[i]=read(); 
	for(int i=1;i<=n;i++)
	{
		x=read(); y=read(); z=read(); 
		printf("%lld\n",work(x,y,z));
	}	
	return 0;
}
十号板子题:
区间众数

(最后一道好难啊,,,看了好长时间题解还没看懂)
还是要感谢大佬的题解:传送门
80分代码如下:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int sea = 1e5 + 5;
int n,m,a[sea],l,r,belong[sea],num[sea],block,t[sea],dp[1010][1010];
vector<int>v[sea];
inline LL read()
{
	LL s=0,w=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1; ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
void ins(int t)
{
	int mode=0,maxx=0;
	memset(num,0,sizeof(num));
	for(int i=(t-1)*block+1;i<=n;i++)
	{
		num[a[i]]++;
		if(num[a[i]]>maxx)
		maxx=num[a[i]],mode=a[i];
		if(maxx==num[a[i]]&&mode>a[i]) mode=a[i];
		dp[t][belong[i]]=mode; 
	}
}
LL so(int l,int r,int tt)
{
	vector<int>::iterator xx = upper_bound(v[tt].begin(), v[tt].end(), r);
	vector<int>::iterator yy = lower_bound(v[tt].begin(), v[tt].end(), l);
	return xx-yy;
}
LL ask(int x,int y)
{
	int t1=belong[x],t2=belong[y];
	int mode=dp[t1+1][t2-1];
	int maxx=so(x,y,mode);
	for(int i=x;i<=min(y,t1*block);i++)
	{
		int t=so(x,y,a[i]);
        if(maxx<t||(maxx==t&&mode>a[i])) 
		maxx=t,mode=a[i]; 
	}
	if(t1!=t2)
	{
		for(int i=(t2-1)*block+1;i<=y;i++)
		{
			int t=so(x,y,a[i]);
			if(maxx<t||(maxx==t&&mode>a[i]))
			maxx=t,mode=a[i];
		}
	}
	return mode;
}
int main()
{
	n=read();
	block=50;
	for(int i=1;i<=n;i++)
	{
		a[i]=read(); t[i]=a[i];
		belong[i]=(i-1)/block+1;
	}
	sort(t+1,t+1+n);
	m=unique(t+1,t+1+n)-t-1;//离散化 
	for(int i=1;i<=n;i++) 
    a[i]=lower_bound(t+1,t+1+m,a[i])-t;
	for(int i=1;i<=n;i++) v[a[i]].push_back(i);
	for(int i=1;i<=belong[n];i++) ins(i);
	for(int i=1;i<=n;i++)
	{
		l=read(); r=read();
		printf("%d\n",t[ask(l,r)]);
	}
	return 0;
}	

92分的代码如下:(大佬的,我的调了两个小时没跳出来,连静态查错都不行了~~o(>_<)o ~~)

#include <bits/stdc++.h>
using namespace std;
inline int read() {//读入挂
    int ret = 0, c, f = 1;
    for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
    if(c == '-') f = -1, c = getchar();
    for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
    if(f < 0) ret = -ret;
    return ret;
}
const int maxn = 1e5 + 5;
int m, block, n, a[maxn], l, r, belog[maxn], t[maxn];
int cnt[505][maxn], f[505][505]; //cnt[i][j]表示[0,i]块上数字j的数量
//f[i][j]表示第i块到第j块的众数是几
void init() {
    for(int i = 1; i <= belog[n]; i++) {
        for(int j = 1; j <= m; j++) {
            cnt[i][j] += cnt[i - 1][j];
        }
    }
    for(int i = 1; i <= belog[n]; i++) { //初始化f数组
        int num[maxn];
        int mode = 0;
        int MAXnum = 0;
        memset(num, 0, sizeof(num));
        for(int j = (i - 1) * block + 1; j <= n; j++) {
            num[a[j]]++;
            if(num[a[j]] > MAXnum || (num[a[j]] == MAXnum && mode > a[j])) {
                mode = a[j];
                MAXnum = num[a[j]];
            }
            f[i][belog[j]] = mode;
        }
    }
}
void force(int l, int r) {
    int num[maxn];
    int mode = 0;
    int MAXnum = 0;
    memset(num, 0, sizeof(num));
    for(int i = l; i <= r; i++) {
        num[a[i]]++;
        if(num[a[i]] > MAXnum || (num[a[i]] == MAXnum && mode > a[i])) {
            mode = a[i];
            MAXnum = num[a[i]];
        }
    }
    printf("%d\n", t[mode]);
}
void solve(int t1, int t2, int l, int r, int*num, int &mode, int &MAXnum) {
    for(int i = l; i <= r; i++) {
        num[a[i]]++;
        int tmp = num[a[i]] + cnt[t2 - 1][a[i]] - cnt[t1][a[i]];
        if(tmp > MAXnum || (tmp == MAXnum && a[i] < mode)) {
            mode = a[i];
            MAXnum = tmp;
        }
    }
}
void query(int l, int r) {
    int t1 = belog[l];
    int t2 = belog[r];
    if(t1 == t2 || t1 + 1 == t2) {//要分两种情况讨论
        force(l, r);
        return;
    }
    int mode = f[t1 + 1][t2 - 1];
    int MAXnum = cnt[t2 - 1][mode] - cnt[t1][mode];
    int num[maxn];
    memset(num, 0, sizeof(num));
    int L = l, R = t1 * block;
    solve(t1, t2, L, R, num, mode, MAXnum);
    L = (t2 - 1) * block + 1;
    R = r;
    solve(t1, t2, L, R, num, mode, MAXnum);
    printf("%d\n", t[mode]);
}
int main() {
    n = read();
    block = 2 * sqrt(n);
    for(int i = 1; i <= n; i++) {
        a[i] = read();
        t[i] = a[i];
        belog[i] = (i - 1) / block + 1;
    }
    sort(t + 1, t + 1 + n);
    m = unique(t + 1, t + 1 + n) - t - 1;
    for(int i = 1; i <= n; i++) {
        a[i] = lower_bound(t + 1, t + 1 + m, a[i]) - t;
        cnt[belog[i]][a[i]]++;
    }
    init();
    for(int i = 1; i <= n; i++) {
        l = read();
        r = read();
        query(l, r);
    }
    return 0;
}

还有一道比较好的分块题:

蒲公英(区间在线众数)

来补题了,,,,

好早以前的一道分块题,由于今天要讲莫队就顺便想到了还有一道这样的题就过来写了一下,(没想到是个黑题,!!!∑(゚Д゚ノ)ノ)
题解:
(尽管这如果不是强制在线的话就是一个裸的莫队题,但是这是个强制在线,结果莫队就败了,就只能用分块了,,)

分块众数+离散化

原来是用vector写的,然后由于vector太慢而且不会离散化就死掉了,就改成数组+离散化写,思想呢,是参考洛谷上的大佬: hkr04 的思想。具体解释看代码。
AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=40000+10;
int a[maxn],b[maxn],t[maxn],l[maxn],r[maxn],s[200+10][maxn],belong[maxn];//s[i][j]表示在前i块中b[j]出现的次数
int f[210][210];//f[i][j]表示从第i个块到第j个块中的众数
int block;//每块长度
int read()
{
    int x=0,p=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')p=-1;ch=getchar();}
    while('0'<=ch&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*p;
}
int main()
{
    int n=read(),m=read();
    block=sqrt(n);//每一块的长度
    for(int i=1;i<=n;i++) b[i]=a[i]=read();
    sort(b+1,b+1+n);
    int sum=unique(b+1, b+1+n)-b-1,cnt=(n-1)/block+1;
	//sum是不同的数值个数,cnt是总块数
    for (int i=1;i<=n;i++)
    a[i]=lower_bound(b+1, b+1+sum, a[i])-b;//这一部分是离散化,a存储的是a[i]在b中的位置
	for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
	for(int i=1;i<=belong[n];i++) l[i]=(i-1)*block+1,r[i]=i*block;
	for(int i=1;i<=n;i++) s[belong[i]][a[i]]++;//预处理出f和s
	for(int i=1;i<=cnt;i++)
    for(int j=1;j<=sum;j++)
	s[i][j]+=s[i-1][j];//前缀和
    for(int i=1;i<=cnt;i++)
    for(int j=i;j<=cnt;j++)
    {
        int mx=f[i][j-1];
        for (int k=l[j];k<=min(r[j],n);k++)//不需要从头开始扫
        if ((s[j][a[k]]-s[i-1][a[k]]>s[j][mx]-s[i-1][mx])||(s[j][a[k]]-s[i-1][a[k]]==s[j][mx]-s[i-1][mx]&&a[k]<mx))
        mx=a[k];
        f[i][j]=mx;
    }
    int x=0;
    while(m--)
    {
        int L=(read()+x-1)%n+1,R=(read()+x-1)%n+1;
        if (L>R) swap(L, R);
        int bl=belong[L],br=belong[R],mx=0;//bl是l所属块编号,br是r所属块的编号,mx是众数在b中的下标
        if (br-bl<=1)//如果区间长度小于等于两格,直接暴力
        {
            for (int i=L;i<=R;i++) t[a[i]]++;
            for (int i=L;i<=R;i++)
            if (t[a[i]]>t[mx]||(t[a[i]]==t[mx]&&a[i]<mx)) mx=a[i];
            for (int i=L;i<=R;i++) t[a[i]]=0;//将桶清空
        }
        else
        {
            for (int i=L;i<=min(r[bl],n);i++) t[a[i]]++;
            for (int i=l[br];i<=R;i++) t[a[i]]++;
            mx=f[bl+1][br-1];
            for (int i=L;i<=min(r[bl],n);i++)//考虑蓝色部分出现的数是否会成为众数
            {
                int pre=t[mx]+s[br-1][mx]-s[bl][mx],now=t[a[i]]+s[br-1][a[i]]-s[bl][a[i]];
                if (now>pre||(now==pre&&mx>a[i]))
                    mx=a[i];
            }
            for (int i=l[br];i<=R;i++)
            {
                int pre=t[mx]+s[br-1][mx]-s[bl][mx],now=t[a[i]]+s[br-1][a[i]]-s[bl][a[i]];
                if (now>pre||(now==pre&&mx>a[i]))
                    mx=a[i];
            }
            //将桶清空
            for (int i=L;i<=min(r[bl], n);i++) t[a[i]]=0;
            for (int i=l[br];i<=R;i++) t[a[i]]=0;
        }
        x=b[mx];
        printf("%d\n",x);
    }
    return 0;
}

(提醒:看了看hzwer学长的代码就觉得,,,过不去大数据啊)

做个小总结:
整个分块学习,还是比较顺的,(毕竟还没有学到比较难懂的地方,但倒数第二道题几乎只有思路代码就挂了,,)总结起来呢就是还是理解分块的中心思想比较重要,而且还比较好理解,就是把区间分成三部分,一部分用等长度的块解决,剩下的两部分边界的直接用暴力处理即可,所以是sqrt(n)的复杂度还是比较好理解的,以后做数据结构题的时候就多往分块的方向想想,(毕竟GZ大佬曾经说过:分块能解决一切问题)。

Winners are made ,not born. —— Blng

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值