分块(优雅的暴力) 学习博客。。。持续更新

学习参照以下三个博客:分块——优雅的暴力  分块入门1~9 分块数列入门1~9

分块题目训练:

1.逆序对——求区间最小值

分块入门1:

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,单点查值。

//给定一个长为n的序列,以及n个操作,操作涉及区间加法,单点查值
#include
using namespace std;
const int maxn = 50086;
int a[maxn],n,m;
int add[maxn];
int pos[maxn];//,sum[maxn];
int L[maxn],R[maxn];

inline void change(int l,int r,int d){
	int p = pos[l], q = pos[r];
	
	if(p==q)//如果要修改的区间在同一个块里面 
	    for(int i=l;i<=r;++i) a[i]+=d;
	    
	else {//如果要修改的区间不在同一个块里面 
		for(int i=p+1;i<=q-1;++i) add[i] += d;//直接给块赋值 
		for(int i=l;i<=R[p];i++) a[i]+=d;
		for(int i=L[q];i<=r;++i) a[i]+=d;
	}
} 
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	
	int t = sqrt(n);//分为t块 
	
	for(int i=1;i<=t;++i){//开始分块,完整块 
		L[i] = (i-1)*t + 1;
		R[i] = i*t; 
	}
	
	if(R[t]>opt;
		if(!opt) {
			cin>>l>>r>>c;
			change(l,r,c);
		}
		else {
			cin>>r;
			int q = pos[r];
			cout<

昨天才学的分块,今天就出题了,也太爽了吧小牛练习赛E题

 

分块入门2:

给出一个长为 n 的数列,以及 m个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。

#include
using namespace std;
typedef long long ll;
const int maxn = 5e4+100;
int n,m,blo;
int v[maxn],bl[maxn];
 
int atag[maxn];//块的增量 

vectorve[505];//存放每一块的元素 

void reset(int x){//将第x块的元素重新排序 
	ve[x].clear();
	for(int i=(x-1)*blo+1;i<=min(x*blo,n);++i)
	ve[x].push_back(v[i]);
	sort(ve[x].begin(),ve[x].end());
}

void add(int a,int b,int c){//区间更新 
	for(int i=a;i<=min(bl[a]*blo,b);++i) v[i]+=c;
	reset(bl[a]);
	if(bl[a]!=bl[b]){
		for(int i=(bl[b]-1)*blo+1;i<=b;++i) v[i]+=c;
		reset(bl[b]);
	}
	for(int i=bl[a]+1;i<=bl[b]-1;++i) atag[i] += c;
}

int query(int a,int b,int c){ 
	int ans = 0;
	//对于不完整的块直接暴力判断 
	for(int i=a;i<=min(bl[a]*blo,b);i++){
		if(v[i]+atag[bl[a]]>n>>m;
	blo = sqrt(n);
	for(int i=1;i<=n;++i) cin>>v[i];
	for(int i=1;i<=n;++i){
		bl[i] = (i-1)/blo + 1;
		ve[bl[i]].push_back(v[i]);
	}
	for(int i=1;i<=bl[n];++i){
		sort(ve[i].begin(),ve[i].end());//将每一块的元素排序 
	}
	for(int i=1;i<=m;++i){
		int opt,l,r,d;
		cin>>opt>>l>>r>>d;
		if(opt==1) add(l,r,d);
		else printf("%d\n",query(l,r,d));
	}
}

/*10 5
1 2 3 4 5 6 7 8 9 10
1 1 10 1
2 1 10 3
1 1 5 3
1 6 10 4
2 1 10 15
*/

 

 

分块入门3:

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(比其小的最大元素)。

set会比vector快一点。set插入时间复杂度是o(1),并且set对于删除和插入元素更加灵活。

vector写法:

#include
using namespace std;
const int maxn = 5e4+100;
int n,m,blo;
int val[maxn],bl[maxn];
int add[maxn];
vectorve[510];
void reset(int x){
	ve[x].clear();
	for(int i=(bl[x]-1)*blo+1;i<=bl[x]*blo;i++) ve[x].push_back(val[i]);
	sort(ve[x].begin(),ve[x].end());
}
void change(int l,int r,int d){
	for(int i=l;i<=min(r,bl[l]*blo);++i) val[i]+=d;
	reset(bl[l]);
	if(bl[l]!=bl[r]){
		for(int i=(bl[r]-1)*blo+1;i<=r;i++) val[i]+=d;
		reset(bl[r]);
	}
	for(int i=bl[l]+1;i<=bl[r]-1;i++) add[i]+=d;
}
int query(int l,int r,int d){
	int mx = -1;
	for(int i=l;i<=min(r,bl[l]*blo);++i) 
	    if(val[i]+add[bl[l]]>n>>m;blo=sqrt(n);
	for(int i=1;i<=n;i++) cin>>val[i];
	for(int i=1;i<=n;i++){
		bl[i] = (i-1)/blo + 1;
		ve[bl[i]].push_back(val[i]);
	}
	for(int i=1;i<=bl[n];i++){
		sort(ve[i].begin(),ve[i].end());
	}
	for(int i=1;i<=m;i++){
		int opt,l,r,d;
		cin>>opt>>l>>r>>d;
		if(opt==1) change(l,r,d);
		else printf("%d\n",query(l,r,d));
	}
 } 

set写法:

#include
using namespace std;
const int N = 1e5+10;
int val[N],n,m,blo;
int add[N],bl[N];
setst[550];
void change(int l,int r,int d){
	for(int i=l;i<=min(r,bl[l]*blo);i++){
		st[bl[l]].erase(val[i]);
		val[i]+=d;
		st[bl[l]].insert(val[i]);
	}
	if(bl[l]!=bl[r]){
		for(int i=(bl[r]-1)*blo+1;i<=r;i++){
			st[bl[r]].erase(val[i]);
			val[i]+=d;
			st[bl[r]].erase(val[i]);
		}
	}
	for(int i=bl[l]+1;i<=bl[r]-1;i++) add[i]+=d;
}
int query(int l,int r,int d){
	int ans = -1;
	for(int i=l;i<=min(r,bl[l]*blo);i++){
		if(val[i]+add[bl[i]]::iterator it = st[i].lower_bound(x);
		if(it==st[i].begin()) continue;
		--it;
		ans = max(ans,*it+add[i]);
	}
	return ans;
}
int main()
{
	cin>>n>>m;
	blo = sqrt(n);
	for(int i=1;i<=n;++i) cin>>val[i];
	for(int i=1;i<=n;++i){
		bl[i] = (i-1)/blo + 1;
		st[bl[i]].insert(val[i]);
	}
	for(int i=1;i<=m;i++){
		int opt,l,r,d;
		cin>>opt>>l>>r>>d;
		if(opt==1) change(l,r,d);
		else printf("%d\n",query(l,r,d));
	}
}

 

分块入门 4:

给出一个长为n的数列,以及m个操作,操作涉及区间加法,区间求和。

#include
using namespace std;
const int N = 1e5+100;
int val[N],L[N],R[N],pos[N],sum[N],add[N];
int n,m;
inline void change(int l,int r,int d){
	int p = pos[l] ,q = pos[r];
	if(p==q) for(int i=l;i<=r;i++) val[i]+=d,sum[p]+=d;
	else {
		for(int i=l;i<=R[p];i++)  val[i]+=d,sum[p]+=d;
		for(int i=L[q];i<=r;i++) val[i]+=d,sum[q]+=d;
		for(int i=p+1;i<=q-1;i++) add[i]+=d;
	}
}
inline int query(int l,int r){
	int p = pos[l] , q = pos[r];
	int ans = 0;
	if(p==q) for(int i=l;i<=r;i++) ans += val[i]+add[i];
	else {
		for(int i=p+1;i<=q-1;i++) ans+=sum[i]+(R[i]-L[i]+1)*add[i];
		for(int i=l;i<=R[p];i++) ans += val[i]+add[p];
		for(int i=L[q];i<=r;i++) ans += val[i]+add[q];
	}
	return ans;
}
int main()
{
	cin>>n>>m;
	int t = sqrt(n);
	for(int i=1;i<=n;i++) cin>>val[i];
	for(int i=1;i<=t;i++){
		L[i] = (i-1)*t+1;
		R[i] = i*t;
	}
	if(R[t]>opt;
		if(opt==1) {
			cin>>l>>r>>d;
			change(l,r,d); 
		}
		else {
			cin>>l>>r;
		   printf("%d\n",query(l,r));
		}
	}
}

分块入门 5 :

给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。

对于区间开方,我们可以想到,对于一个区间的数,经过数次开方后,他们会变为0或1,所以采取一种分块优化的暴力做法,只要每个整块暴力开方后,记录一下元素是否都变成了 0 / 1,区间修改时跳过那些全为 0 / 1 的块即可。

#include
using namespace std;
const int N = 1e5 + 100;
int n,m,blo;
int val[N],bl[N],sum[N],vis[N];
void solve_sqrt(int x){//暴力判断这一块是否变成了1 或 0 
	if(vis[x]) return;
	vis[x]=1;
	sum[x]=0;
	for(int i=(x-1)*blo+1;i<=x*blo;i++){
		val[i] = sqrt(val[i]);
		sum[x]+=val[i];
		if(val[i]>1) vis[x]=0;
	}
}
void change(int l,int r){
	for(int i=l;i<=min(r,bl[l]*blo);i++){
		sum[bl[l]]-=val[i];
		val[i] = sqrt(val[i]);
		sum[bl[l]]+=val[i];
	}
	if(bl[l]!=bl[r]){
		for(int i=(bl[r]-1)*blo+1;i<=r;i++){
			sum[bl[r]]-=val[i];
			val[i] = sqrt(val[i]);
			sum[bl[r]]+=val[i];
		}
	}
	for(int i=bl[l]+1;i<=bl[r]-1;i++)
	   solve_sqrt(i);
}
int query(int l,int r){
	int ans = 0;
	for(int i=l;i<=min(bl[l]*blo,r);i++) ans+=val[i];
	if(bl[l]!=bl[r])
	   for(int i=(bl[r]-1)*blo+1;i<=r;i++) ans+=val[i];
	for(int i=bl[l]+1;i<=bl[r]-1;i++) ans+=sum[i];
	return ans;
}
int main()
{
	cin>>n>>m;
	blo =sqrt(n); 
	for(int i=1;i<=n;i++) cin>>val[i];
	for(int i=1;i<=n;i++){
		bl[i]=(i-1)/blo + 1;
		sum[bl[i]]+=val[i];
	}
	for(int i=1;i<=m;i++){
		int opt,l,r;
		cin>>opt>>l>>r;
		if(opt==1) change(l,r);
		else printf("%d\n",query(l,r));
	}
}

 

分块入门6:

给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。

#include
using namespace std;
const int maxn = 1e5+100;
int val[maxn],s[maxn];
int n,m,blo,q;
struct node{
	int s,t;
};
vectore[1010];
inline node query(int x){//返回某一元素在块中的位置 
	int i=1;
	while(x>(int)e[i].size())
	   x-=(int)e[i].size(),i++;
	return node{i,x-1};
}

inline void rebuild(){//重新分块 
	int top=0;
	for(int i=1;i<=q;++i){
		for(int j=0;j20*blo)//此块元素过多,重新分块 
		rebuild();
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i) 
		cin>>val[i];
	blo = sqrt(n);q=(n-1)/blo+1;
	for(int i=1;i<=n;i++) 
		e[(i-1)/blo+1].push_back(val[i]);//分块 
	for(int i=1;i<=m;i++){
		int opt,l,r;
		cin>>opt;
		if(opt==1){
			cin>>l>>r;
			change(l,r);
		}
		else {
			cin>>r;
			node x = query(r);
			cout<

 

分块入门 7

给出一个长为n的数列,以及n个操作,操作涉及区间乘法,区间加法,单点询问。

#include
using namespace std;
const int maxn = 1e5+100;
int val[maxn],bl[1000],add[1000],mul[1000],sum[1000];
int n,m,blo;
void resert(int x){
	for(int i=(bl[x]-1)*blo+1;i<=min(n,bl[x]*blo);i++)
	val[i] = val[i]*mul[x]+add[x];
	mul[x]=1,add[x]=0;
}
void change(int l,int r,int d,int opt){
	resert(bl[l]);
	for(int i=l;i<=min(r,bl[l]*blo);i++){
		if(opt==1) val[i]+=d;
		else val[i]*=d;
	}
	if(bl[l]!=bl[r]){
		resert(bl[r]);
		for(int i=(bl[r]-1)*blo+1;i<=r;i++){
			if(opt==1) val[i]+=d;
		    else val[i]*=d;
		}
	}
	for(int i=bl[l]+1;i<=bl[r]-1;i++){
		if(opt==1) add[i]+=d;
		else {
			add[i]*=d;
			mul[i]*=d;
		}
	}
}
int main()
{
	for(int i=1;i<=1000;i++) mul[i]=1;
	cin>>n>>m;
	blo = sqrt(n);
	for(int i=1;i<=n;i++) {
		cin>>val[i];
		bl[i]=(i-1)/blo+1;
		sum[bl[i]]+=val[i];
	}
	for(int i=1;i<=m;i++){
		int opt,l,r,d;
		cin>>opt;
		if(opt==1||opt==2){//加法
		    cin>>l>>r>>d;
		    change(l,r,d,opt); 
		} 
		else {
			cin>>r;
			cout<

 

分块入门 8

给出一个长为n的数列,以及n个操作,操作涉及区间询问等于一个数c的元素,并将这个区间的所有元素改为c。

 

#include
using namespace std;
const int N = 1e5+100;
int val[N],vis[1000],bl[N];
int q,n,m,blo;
void reset(int x){
	if(vis[x]==-1) return;
	for(int i=(x-1)*blo+1;i<=blo*x;++i)
	val[i]=vis[x];
	vis[x]=-1;
}
int query(int l,int r,int d){
	int ans = 0;
    reset(bl[l]);
    for(int i=l;i<=min(r,bl[l]*blo);++i) {
		if(val[i]==d) ans++;
		val[i]=d;
	}
	if(bl[l]!=bl[r]){
		reset(bl[r]);
		for(int i=(bl[r]-1)*blo+1;i<=r;++i) {
			if(val[i]==d) ans++;
			val[i]=d;
		}
	}
	for(int i=bl[l]+1;i<=bl[r]-1;++i){
		if(vis[i]!=-1){
			if(vis[i]!=d) vis[i]=d;
			else ans+=blo;
		}
		else {
			for(int j=(i-1)*blo+1;j<=i*blo;j++)
			   if(val[j]!=d) val[j]=d;
			   else ans++;
			vis[i]=d;
		}
	}
	return ans;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>val[i];
	for(int i=1;i<=1000;i++) vis[i]=-1;
	blo = sqrt(n);
	for(int i=1;i<=n;++i) bl[i] = (i-1)/blo+1;
	for(int i=1;i<=m;i++){
		int l,r,d;
		cin>>l>>r>>d;
		printf("%d\n",query(l,r,d));
	}
}

 

你可能感兴趣的:(分块)