分块入门(模板)

编程入门 行业动态 更新时间:2024-10-17 06:24:57

分块<a href=https://www.elefans.com/category/jswz/34/1770026.html style=入门(模板)"/>

分块入门(模板)

以前没玩过分块,只知道基本的概念,也没刷过题,先挂几个题。
ps:这比我当初学线段树、主席树时要简单。


样例一:区间修改+单点查询

const int maxn=5e5+7;
ll n,a[maxn],c,bl[maxn],b[maxn];
void add(ll l,ll r,ll x){for(int i=l;i<=min(bl[l]*c,r);i++)	a[i]+=x;if(bl[l]!=bl[r])	for(int i=(bl[r]-1)*c+1;i<=r;i++)	a[i]+=x;for(int i=bl[l]+1;i<bl[r];i++)	b[i]+=x;
}
int main(){n=read();	c=sqrt(n);for(int i=1;i<=n;i++)	a[i]=read();for(int i=1;i<=n;i++)	bl[i]=(i-1)/c+1;while(n--){ll op,l,r,x;op=read();	l=read();	r=read();	x=read();if(!op)	add(l,r,x);else	printf("%lld\n",a[r]+b[bl[r]]);}
}

样例二:区间修改+区间查询 小于 c元素的个数(vector 二分)

询问操作:
1、不完整的块枚举统计即可;
2、而要在每个整块内寻找小于一个值的元素数,于是我们不得不要求块内元素是有序的,这样就能使用二分法对块内查询,需要预处理时每块做一遍排序,复杂度O(nlogn),每次查询在√n个块内二分,以及暴力2√n个元素,总复杂度O(nlogn + n√nlog√n)。

每次修改,边块的vector必须重构。必须在vector里面二分,不能改变原数组的位置。

#define	bol(x)	((x-1)*c+1)
const int maxn=5e5+7;
int n,a[maxn],c,b[maxn],add[507];
vector<int>v[507];
//重构 
void reset(int x){v[x].clear();for(int i=bol(x);i<=min(bol(x+1)-1,n);i++)	v[x].push_back(a[i]);sort(v[x].begin(),v[x].end());
}
void modify(int l,int r,int x){int bl=b[l],br=b[r];if(bl==br){for(int i=l;i<=r;i++)	a[i]+=x;reset(bl);	return;}for(int i=l;i<bol(bl+1);i++)	a[i]+=x;for(int i=bol(br);i<=r;i++)		a[i]+=x;for(int i=bl+1;i<br;i++)		add[i]+=x;reset(bl);	reset(br);
}
int query(int l,int r,int x){int bl=b[l],br=b[r],ans=0;if(bl==br){for(int i=l;i<=r;i++)	if(a[i]+add[bl]<x)	ans++;return ans;}for(int i=l;i<bol(bl+1);i++)	if(a[i]+add[bl]<x)	ans++;for(int i=bol(br);i<=r;i++)		if(a[i]+add[br]<x)	ans++;for(int i=bl+1;i<br;i++){int tmp=x-add[i];ans+=lower_bound(v[i].begin(),v[i].end(),tmp)-v[i].begin();}return ans;
}
int main(){n=read();	c=n/sqrt(n);for(int i=1;i<=n;i++){a[i]=read();b[i]=(i-1)/c+1;v[b[i]].push_back(a[i]);}for(int i=1;i<=b[n];i++)	sort(v[i].begin(),v[i].end());for(int i=1;i<=n;i++){int op,l,r,x;op=read();	l=read();	r=read();	x=read();if(!op)	modify(l,r,x);else	printf("%d\n",query(l,r,x*x));}
}

样例三:区间修改+区间查询 小于 c的最大数(set二分)

和上一题类似,将vector换成set,利用set自动排序,同时每次设的最小值为(1<<31),注意不是(1<<32)这会溢出。

#define	bol(x)	((x-1)*c+1)
const int maxn=1e5+7;
const int inf=(1<<31);
int n,a[maxn],c,b[maxn],add[1007];
set<int>v[1007];
//重构 
void reset(int x){v[x].clear();for(int i=bol(x);i<=min(bol(x+1)-1,n);i++)	v[x].insert(a[i]);
}
void modify(int l,int r,int x){int bl=b[l],br=b[r];if(bl==br){for(int i=l;i<=r;i++)	a[i]+=x;reset(bl);	return;}for(int i=l;i<bol(bl+1);i++)	a[i]+=x;for(int i=bol(br);i<=r;i++)		a[i]+=x;for(int i=bl+1;i<br;i++)		add[i]+=x;reset(bl);	reset(br);
}
int query(int l,int r,int x){int bl=b[l],br=b[r],ans=inf;if(bl==br){for(int i=l;i<=r;i++){int tl=a[i]+add[bl];if(tl<x&&tl>ans)	ans=tl;}return (ans==inf)?-1:ans;}for(int i=l;i<bol(bl+1);i++)	if(a[i]+add[bl]<x&&a[i]+add[bl]>ans)ans=a[i]+add[bl];for(int i=bol(br);i<=r;i++)	if(a[i]+add[br]<x&&a[i]+add[br]>ans)ans=a[i]+add[br];for(int i=bl+1;i<br;i++){int tmp=x-add[i];set<int>::iterator it=v[i].lower_bound(tmp);if(it==v[i].begin())	continue;it--;ans=max(ans,*it+add[i]);}return (ans==inf)?-1:ans;
}
int main(){n=read();	c=n/sqrt(n);for(int i=1;i<=n;i++){a[i]=read();b[i]=(i-1)/c+1;v[b[i]].insert(a[i]);}for(int i=1;i<=n;i++){int op,l,r,x;op=read();	l=read();	r=read();	x=read();if(!op)	modify(l,r,x);else	printf("%d\n",query(l,r,x));}
}

样例四:区间修改+区间查询和

基础运用

#define	bol(x)	((x-1)*c+1)
const int maxn=1e5+7;
ll n,a[maxn],c,b[maxn],add[1007],v[1007];
void modify(ll l,ll r,ll x){ll bl=b[l],br=b[r];if(bl==br){for(ll i=l;i<=r;i++)	a[i]+=x,v[bl]+=x;return;}for(ll i=l;i<bol(bl+1);i++)	a[i]+=x,v[bl]+=x;for(ll i=bol(br);i<=r;i++)	a[i]+=x,v[br]+=x;for(ll i=bl+1;i<br;i++)		add[i]+=x,v[i]+=x*c;
}
ll query(ll l,ll r,ll x){ll bl=b[l],br=b[r],ans=0;if(bl==br){for(ll i=l;i<=r;i++)	ans=(ans+a[i]+add[bl])%(x+1);return ans;}for(ll i=l;i<bol(bl+1);i++)	ans=(ans+a[i]+add[bl])%(x+1);for(ll i=bol(br);i<=r;i++)	ans=(ans+a[i]+add[br])%(x+1);for(ll i=bl+1;i<br;i++)		ans=(ans+v[i])%(x+1);return ans;
}
int main(){n=read();	c=sqrt(n);for(ll i=1;i<=n;i++){a[i]=read();b[i]=(i-1)/c+1;v[b[i]]+=a[i];}for(ll i=1;i<=n;i++){ll op,l,r,x;op=read();	l=read();	r=read();	x=read();if(!op)	modify(l,r,x);else	printf("%d\n",query(l,r,x));}
}

样例五:区间开方+区间查询和

一个int范围内的数,最多开5次方(向小取整)。那么就是说:一个块在几次操作后很容易全部变成1,这个时候再操作开方,就不用修改了。

这里定义一个判断数组 f [507], 为1时,表示该块全为1,当然没有变为0的时候只能暴力修改了, 复杂度不会太高。

#define	bol(x)	((x-1)*c+1)
const int maxn=1e5+7;
ll n,a[maxn],c,b[maxn],f[1007],v[1007];
void modify(ll l,ll r){ll bl=b[l],br=b[r];if(bl==br){for(int i=l;i<=r;i++){v[bl]-=a[i];a[i]=(int)sqrt(a[i]);v[bl]+=0ll+a[i];}return;}for(int i=l;i<bol(bl+1);i++){v[bl]-=a[i];a[i]=(int)sqrt(a[i]);v[bl]+=0ll+a[i];}for(int i=bol(br);i<=r;i++){v[br]-=a[i];a[i]=(int)sqrt(a[i]);v[br]+=0ll+a[i];}for(int i=bl+1;i<br;i++){if(f[i])	continue;	//该块全为1f[i]=1;	v[i]=0;for(int j=bol(i);j<bol(i+1);j++){a[j]=(int)sqrt(a[j]);v[i]+=0ll+a[j];if(a[j]>1)	f[i]=0;} }
}
ll query(ll l,ll r){ll bl=b[l],br=b[r],ans=0;if(bl==br){for(int i=l;i<=r;i++)	ans+=a[i];return ans;}for(int i=l;i<bol(bl+1);i++)	ans+=a[i];for(int i=bol(br);i<=r;i++)		ans+=a[i];for(int i=bl+1;i<br;i++)		ans+=v[i];return ans;
}
int main(){n=read();	c=sqrt(n);for(int i=1;i<=n;i++){a[i]=read();b[i]=(i-1)/c+1;v[b[i]]+=a[i];}for(int i=1;i<=n;i++){ll op,l,r,x;op=read();	l=read();	r=read();	x=read();if(!op)	modify(l,r);else	printf("%lld\n",query(l,r));}
}

样例六:动态插入+分块重构

这里注意每个块的个数开始不一定相同了,要了解当前点在第几块还得从头一个块枚举过去。

当一个块相当于其它块过于大时(可以设一个临界值),分块重构,重构需要的复杂度为O(n),重构的次数为√n,所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。

#define	bol(x)	((x-1)*c+1)
const int maxn=1e6+7;
int n,a[maxn],c,m;
vector<int>v[maxn];
typedef pair<int,int>p;
void rebuild(){int top=0;for(int i=1;i<=m;i++){for(int val:v[i])	a[++top]=val;v[i].clear();}c=sqrt(top);	m=ceil(1.0*top/c);for(int i=1;i<=top;i++)	v[(i-1)/c+1].push_back(a[i]);
}
p query(int x){for(int i=1;i<=m;i++){if(x>v[i].size())	x-=v[i].size();else	return make_pair(i,x-1);}
}
void Insert(int i,int x){p t=query(i);v[t.first].insert(v[t.first].begin()+t.second,x);	//插入元素 if(v[t.first].size()>10*c)	rebuild();			//块过大时,重新分块 
}
int main(){n=read();	c=sqrt(n);	m=ceil(1.0*n/c);for(int i=1;i<=n;i++){a[i]=read();v[(i-1)/c+1].push_back(a[i]);}for(int i=1;i<=n;i++){int op,l,r,x;op=read();	l=read();	r=read();	x=read();if(!op)	Insert(l,r);else{p t=query(r);printf("%d\n",v[t.first][t.second]);}}
}

样例七:区间乘+加

和线段树的双标记类似,有优先级

若当前的一个块乘以m1后加上a1,这时进行一个乘m2的操作,则原来的标记变成m1 * m2,a1 * m2
若当前的一个块乘以m1后加上a1,这时进行一个加a2的操作,则原来的标记变成m1,a1+a2

#define	bol(x)	((x-1)*c+1)
const int maxn=1e5+7;
const int mod=10007;
int n,a[maxn],c,m,p[maxn],q[maxn],b[maxn];
void reset(int x){for(int i=bol(x);i<=min(bol(x+1)-1,n);i++)a[i]=(a[i]*p[x]+q[x])%mod;p[x]=1;	q[x]=0;
}
void add(int op,int l,int r,int x){int bl=b[l],br=b[r];if(bl==br){reset(bl);for(int i=l;i<=r;i++){if(op)	a[i]=(a[i]*x)%mod;else	a[i]=(a[i]+x)%mod;}return;}reset(bl);	reset(br);for(int i=l;i<bol(bl+1);i++){if(op)	a[i]=(a[i]*x)%mod;else	a[i]=(a[i]+x)%mod;}for(int i=bol(br);i<=r;i++){if(op)	a[i]=(a[i]*x)%mod;else	a[i]=(a[i]+x)%mod;}for(int i=bl+1;i<br;i++){if(op){p[i]=(p[i]*x)%mod;q[i]=(q[i]*x)%mod;}else	q[i]=(q[i]+x)%mod;}
}
int main(){n=read();	c=sqrt(n);for(int i=1;i<=n;i++){a[i]=read();b[i]=(i-1)/c+1;p[b[i]]=1;	//乘操作记录 }for(int i=1;i<=n;i++){int op,l,r,x;op=read();	l=read();	r=read();	x=read();if(op<2)		add(op,l,r,x);	else{int sum=(a[r]*p[b[r]]+q[b[r]])%mod;printf("%d\n",sum);}}
}

样例八:区间修改为同一值查询区间元素=c的

和线段树区间修改为同一值类似,都要下推,涉及区间求和的都有pushdown,和线段树一模一样啊,不过分块偏暴力点。

#define	bol(x)	((x-1)*c+1)
const int maxn=1e5+7;
const int mod=10007;
int n,a[maxn],c,m,b[maxn],f[maxn];
void pushdown(int x){if(f[x]!=-1){for(int i=bol(x);i<=min(bol(x+1)-1,n);i++)	a[i]=f[x];f[x]=-1;}
}
int query(int l,int r,int x){int bl=b[l],br=b[r],ans=0;if(bl==br){pushdown(bl);for(int i=l;i<=r;i++){if(a[i]==x)	ans++;a[i]=x;}return ans;}pushdown(bl);	pushdown(br);for(int i=l;i<bol(bl+1);i++){if(a[i]==x)	ans++;a[i]=x;}for(int i=bol(br);i<=r;i++){if(a[i]==x)	ans++;a[i]=x;}for(int i=bl+1;i<br;i++){if(f[i]==x)	ans+=c;else if(f[i]==-1){for(int j=bol(i);j<bol(i+1);j++){if(a[j]==x)	ans++;a[j]=x;}}f[i]=x;}return ans;
}
int main(){n=read();	c=sqrt(n);for(int i=1;i<=n;i++){a[i]=read();b[i]=(i-1)/c+1;f[b[i]]=-1;}for(int i=1;i<=n;i++){int l,r,x;l=read();	r=read();	x=read();printf("%d\n",query(l,r,x));}
}

样例九:维护区间众数

用预处理ans[ i ] [ j ] ,来表示第 i 个块到第 j 个块的众数,方法有点暴力,用的枚举。
这题中如果你用map 或unordered_map的话,你设计的块的大小是sqrt(n)的话会超时,你可以设计块长,这样会过,有点玄学,当然你用数组离散化的话,可以不用设计块长。

#include <bits/stdc++.h>
#pragma GCC optimize (2)
#pragma G++ optimize (2)
#pragma GCC optimize("Ofast,unroll-loops,no-stack-protector,fast-math")
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#define debug(x) cout<<#x<<" :"<<x<<endl
using namespace std;
typedef long long ll;
inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;
}
void out(ll x) {int stackk[40];if(x<0){putchar('-');x=-x;}if(!x){putchar('0');return;}int top=0;while(x)	stackk[++top]=x%10,x/=10;while(top)	putchar(stackk[top--]+'0');
}
#define	bol(x)	((x-1)*c+1)
const int maxn=1e5+7;
int n,a[maxn],c,m,b[maxn],ans[2007][2007],tot,cnt[maxn];
vector<int>v[maxn];
unordered_map<int,int>mp,f;
void init(int id){memset(cnt,0,sizeof(cnt));int mx=0,res=0;for(int i=bol(id);i<=n;i++){int to=b[i];cnt[a[i]]++;if(cnt[a[i]]>mx||(cnt[a[i]]==mx&&mp[a[i]]<mp[res])){res=a[i];mx=cnt[a[i]];}ans[id][to]=res;}
}
int getsum(int l,int r,int 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 bl=b[l],br=b[r];int res=ans[bl+1][br-1];int mx=getsum(l,r,res);for(int i=l;i<=min(bl*c,r);i++){int tmp=getsum(l,r,a[i]);if(tmp>mx||(tmp==mx&&mp[a[i]]<mp[res])){res=a[i];mx=tmp;}}if(bl!=br){for(int i=bol(br);i<=r;i++){int tmp=getsum(l,r,a[i]);if(tmp>mx||(tmp==mx&&mp[a[i]]<mp[res])){res=a[i];mx=tmp;}}}return mp[res];
}
int main(){n=read();	c=sqrt(n);c=80;for(int i=1;i<=n;i++){a[i]=read();b[i]=(i-1)/c+1;if(!f[a[i]]){f[a[i]]=++tot;mp[tot]=a[i];}a[i]=f[a[i]];v[a[i]].push_back(i);}for(int i=1;i<=b[n];i++)	init(i);for(int i=1;i<=n;i++){int l,r;l=read();	r=read();if(l>r)	swap(l,r);printf("%d\n",query(l,r));}
}

更多推荐

分块入门(模板)

本文发布于:2024-02-07 11:52:50,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1757050.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:入门   模板

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!