关于分块

image

·········阿达

·········分块这个东西,码量比较小,维护东西还多,很灵活

主要来自LOJ的分块九题

#6277. 数列分块入门 1

区间修改,单点查询

维护懒标记,散块暴力修就是了

const int M=2e5+110;
inline int read(){
	int sum=0,k=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();}
	while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();}
	return sum*k;
}
struct Line_Node{
	int Next,To;
}Ed[M];
int Head[M],Tot=0;
int n,m,a[M];
int pos[M],bl;
struct fenkuai{
	int lz,rz,lazy,sum;
}fk[M];
signed main(){
	n=read();bl=sqrt(n);
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=bl;i++)
		fk[i].lz=(i-1)*bl+1,
		fk[i].rz=i*bl;
	if(fk[bl].rz<n){
		fk[++bl].lz=fk[bl-1].rz+1;
		fk[bl].rz=n;
	}
	for(int i=1;i<=bl;i++)
		for(int j=fk[i].lz;j<=fk[i].rz;j++)
			pos[j]=i;	
	for(int chr=1;chr<=n;chr++){
		int opt=read(),l=read(),r=read(),c=read();
		if(opt==0){
			int ql=pos[l],qr=pos[r];
			if(ql==qr){
				for(int i=l;i<=r;i++) a[i]+=c;
				continue;
			}
			for(int i=l;i<=fk[ql].rz;i++) a[i]+=c;
			for(int i=fk[qr].lz;i<=r;i++) a[i]+=c;
			for(int i=ql+1;i<qr;i++)
					fk[i].lazy+=c;
		}
		else{
			cout<<a[r]+fk[pos[r]].lazy<<endl;
		}
	}
	return 0;
}

#6278. 数列分块入门 2

很好的区间修改,区间小于数个数查询,

散块暴力加,暴力找,重构整块维护排序,整块维护lazy,查询时一遍二分找小于个数

注意:要copy一份原数组来做修改,一个数组会让散块矛盾

const int M=5e4+110;
inline int read(){
	int sum=0,k=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
	}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
	}return sum*k;
}
struct _fenkuai{
	int lz,rz,lazy;
}fk[M];
int n,a[M],pos[M],bl; int  a_[M];
inline void opt_0(int l,int r,int c){
	int ql=pos[l],qr=pos[r];
	if(ql==qr){
		for(int i=l;i<=r;i++) a_[i]+=c;
		for(int i=fk[ql].lz;i<=fk[ql].rz;i++) a[i]=a_[i];
		sort(a+fk[ql].lz,a+fk[qr].rz+1);
		return ;
	}
	for(int i=l;i<=fk[ql].rz;i++) a_[i]+=c;
	for(int i=fk[ql].lz;i<=fk[ql].rz;i++) a[i]=a_[i];
	sort(a+fk[ql].lz,a+1+fk[ql].rz);
	for(int i=fk[qr].lz;i<=r;i++) a_[i]+=c;
	for(int i=fk[qr].lz;i<=fk[qr].rz;i++) a[i]=a_[i];
	sort(a+fk[qr].lz,a+1+fk[qr].rz);
	for(int i=ql+1;i<qr;i++) fk[i].lazy+=c;
}
inline int fin(int l,int r,int c){
	if(a[l]>=c) return 0;
	if(a[r]<c) return r-l+1;
	int ll=l,rr=r,mid,res=0;
	while(ll<=rr) {
		mid=(ll+rr)>>1;
		if(a[mid]<c) res=mid-l+1,ll=mid+1;
		else rr=mid-1;
	}
	return res;
}
inline void opt_1(int l,int r,int c){
	c=c*c;
	int ql=pos[l],qr=pos[r],cnt=0;
	if(ql==qr){
		//printf("%lld\n",fin(l,r,c-fk[ql].lazy));
		for(int i=l;i<=r;i++) if(a_[i]+fk[ql].lazy<c) cnt++;
		cout<<cnt<<"\n";
		return ;
	}
	for(int i=l; i<=fk[ql].rz; i++) 
		if(a_[i] + fk[ql].lazy < c) 
			cnt++;
	for(int i=fk[qr].lz; i<=r; i++) 
		if(a_[i] + fk[qr].lazy < c) 
			cnt++;
	for(int i=ql+1;i<qr;i++)
		cnt+=fin(fk[i].lz,fk[i].rz,c-fk[i].lazy);
	printf("%lld\n",cnt);
	return ;
}

signed main(){
	n=read(),bl=sqrt(n); //块长
	for(int i=1;i<=n;i++) a[i]=read(),a_[i]=a[i]; // a_ 是原版本
	for(int i=1;i<=bl;i++)	fk[i].lz=bl*(i-1)+1,fk[i].rz=bl*i;
	if(fk[bl].rz<n) fk[bl].rz=n; //最后一块多一些
	for(int i=1;i<=bl;i++){
		for(int j=fk[i].lz;j<=fk[i].rz;j++) pos[j]=i;
		sort(a+fk[i].lz,a+fk[i].rz+1);
	}
	for(int i=1;i<=n;i++){
		int opt=read(),l=read(),r=read(),c=read();
		if(opt==0) opt_0(l,r,c);
		if(opt==1) opt_1(l,r,c);
	}
	return 0;
}

#6279. 数列分块入门 3

区间修改,区间查前驱

和分块二很像,散块暴力加,整块lazy,维护块内有序,查询时每个块做二分,比较一下就行了

#6280. 数列分块入门 4

做完前面两道,看到分块四,美味啊 /qq

区间加,区间查,维护lazy,秒了~

#define int long long
using namespace std;
const int M=3e5+110;
inline int read(){//快读 
	int sum=0,k=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
	}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
	}return sum*k; 
}
struct fenkuai{
	int lz,rz,sum,lazy;
}fk[M];//分块 
int Val[M],Pos[M],bl,m,n;
inline void Add(int x,int y,int k){
	int ls=Pos[x],rs=Pos[y],res1=0,res2=0;//左右分块编号,零散数数量 
	if(ls==rs){//x,y在同一分块中 
		for(int i=x;i<=y;i++) Val[i]+=k;//暴力加 
		fk[ls].sum+=(y-x+1)*k;//更新sum 
		return ;
	}
	for(int i=x;i<=fk[ls].rz;i++) //处理左边散数 
		Val[i]+=k,res1++;
	for(int i=fk[rs].lz;i<=y;i++) //处理右边散数 
		Val[i]+=k,res2++;
	fk[ls].sum+=res1*k;//更新左sum 
	fk[rs].sum+=res2*k;//更新右sum 
	for(int i=ls+1;i<rs;i++){//处理中间分块 
		fk[i].lazy+=k;//懒标记 
		fk[i].sum+=(fk[i].rz-fk[i].lz+1)*k;//更新sum 
	}
	return ;	
}
inline void Ask(int x,int y,int z){
	z+=1;
	int ls=Pos[x],rs=Pos[y],res=0;
	if(ls==rs){
		for(int i=x;i<=y;i++) res=(res+Val[i]+fk[ls].lazy)%z;
		printf("%lld\n",res%z);
		return ;
	}
	for(int i=x;i<=fk[ls].rz;i++) 
		res=(res+Val[i]+fk[ls].lazy)%z;
	for(int i=fk[rs].lz;i<=y;i++) 
		res=(res+Val[i]+fk[rs].lazy)%z;
	for(int i=ls+1;i<rs;i++)
		res=(res+fk[i].sum)%z;
	printf("%lld\n",res%z);
	return ;
}
signed main(){
	n=read();bl=sqrt(n);
	for(int i=1;i<=n;i++) Val[i]=read();
	for(int i=1;i<=bl;i++){
		fk[i].rz=i*bl;
		fk[i].lz=(i-1)*bl+1;
	}
	if(fk[bl].rz<n){
		fk[++bl].rz=n;
		fk[bl].lz=fk[bl-1].rz+1;
	} 
	for(int i=1;i<=bl;i++)
		for(int j=fk[i].lz;j<=fk[i].rz;j++)
			Pos[j]=i,fk[i].sum+=Val[j];
	for(int chr=1;chr<=n;chr++){
		int opt=read();
		if(opt==0){
			int x=read(),y=read(),v=read();
			Add(x,y,v);
		}
		else{
			int x=read(),y=read(),z=read();
			Ask(x,y,z);
		}
	}
	return 0; 
}

#6281. 数列分块入门 5

区间开方,区间求和

注意到最大数值为 2的31次方减1,即2,147,483,648

这个东西开五次方就变成了1

暴力区间开方,如果一个区间全是1和0,打上标记即可

查询不变,和这道题一摸一样 P4145 上帝造题的七分钟 2 / 花神游历各国

#6282. 数列分块入门 6

单点插入,单点询问

维护vector分块,乱搞即可

#include<bits/stdc++.h>
using namespace std;
const int M=1e5+7;
inline int read(){
	int sum=0,k=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
	}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
	}return sum*k;
}
inline void wr(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) wr(x/10);
    putchar(x%10+'0');
    return;
}
int n,bl,_a[M];
int Lz[M],Rz[M];
vector<int>a[M];
inline void opt_0(int l,int r){
	for(int i=1;i<=bl;i++){
		if(l<=a[i].size()){
			auto pl=a[i].begin()+l-1;
			a[i].insert(pl,r);
			return ;
		}
		else l-=a[i].size();
	}
}
inline int opt_1(int r){
	for(int i=1;i<=bl;i++){
		if(r<=a[i].size())
			return *(a[i].begin()+r-1);
		else r-=a[i].size();
	}
}
signed main(){
	n=read();bl=sqrt(n);
	for(int i=1;i<=n;i++) _a[i]=read();
	for(int i=1;i<=bl;i++) Lz[i]=Rz[i-1]+1,Rz[i]=i*bl;
	Rz[bl]=n;
	for(int i=1;i<=bl;i++)
		for(int j=Lz[i];j<=Rz[i];j++)
			a[i].push_back(_a[j]);
	for(int i=1;i<=n;i++){
		int opt=read(),l=read(),r=read(),c=read();
		if(opt==0) opt_0(l,r);
		if(opt==1) wr(opt_1(r)),printf("\n");
	}
	return 0;
}

#6283. 数列分块入门 7

区间乘法,区间加法,单点询问

很经典的数据结构,维护加lazy与乘lazy,修改时要将加lazy也乘上要乘的值

还有处理完散块要把lazy下放,reset块一下就好了

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M=1e5+110,mod=10007;
inline int read(){
	int sum=0,k=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
	}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
	}return sum*k;
}
int Head[M],Tot=0;
struct _Node{
	int Next,To;
}Ed[M];
inline void Adde(int u,int v){
	Ed[++Tot]={Head[u],v};
	Head[u]=Tot;
}
struct _fenkuai{
	int lz=0,rz=0;
	int lazy_add=0;
	int lazy_mul=1;
}fk[M];
int pos[M],bl,n,a[M];
inline void reset(int pl){
	for(int i=fk[pl].lz;i<=fk[pl].rz;i++){
		a[i]=(a[i]*fk[pl].lazy_mul+fk[pl].lazy_add)%mod;
	}fk[pl].lazy_mul=1,fk[pl].lazy_add=0;
	return ;
}
inline void opt_0(int l,int r,int c){
	int ql=pos[l],qr=pos[r];
	if(ql==qr){
		reset(ql);
		for(int i=l;i<=r;i++)
			a[i]+=c,a[i]%=mod;
		return ;
	}
	reset(ql),reset(qr);
	for(int i=l;i<=fk[ql].rz;i++) a[i]+=c,a[i]%=mod;
	for(int i=fk[qr].lz;i<=r;i++) a[i]+=c,a[i]%=mod;
	for(int i=ql+1;i<qr;i++) fk[i].lazy_add+=c,fk[i].lazy_add%=mod;
}
inline void opt_1(int l,int r,int c){
	int ql=pos[l],qr=pos[r];
	if(ql==qr){
		reset(ql);
		for(int i=l;i<=r;i++)
			a[i]*=c,a[i]%=mod;
		return ;
	}
	reset(ql),reset(qr);
	for(int i=l;i<=fk[ql].rz;i++) a[i]*=c,a[i]%=mod;
	for(int i=fk[qr].lz;i<=r;i++) a[i]*=c,a[i]%=mod;
	for(int i=ql+1;i<qr;i++) fk[i].lazy_add*=c,fk[i].lazy_add%=mod,fk[i].lazy_mul*=c,fk[i].lazy_mul%=mod;
}
inline void opt_2(int r){
	int pl=pos[r];
	int res=fk[pl].lazy_add+fk[pl].lazy_mul*a[r];
	printf("%lld\n",res%mod);
	return;
}
signed main(){
	n=read();bl=sqrt(n);
	for(int i=1;i<=n;i++) a[i]=read(),a[i]%=mod;
	for(int i=1;i<=bl;i++)fk[i].lz=bl*(i-1)+1,fk[i].rz=bl*i;
	if(fk[bl].rz<n) fk[++bl].rz=n,fk[bl].lz=fk[bl-1].rz+1;
	for(int i=1;i<=bl;i++)
		for(int j=fk[i].lz;j<=fk[i].rz;j++) 
			pos[j]=i;
	for(int i=1;i<=n;i++){
		int opt=read(),l=read(),r=read(),c=read();
		if(opt==0) opt_0(l,r,c);
		if(opt==1) opt_1(l,r,c);
		if(opt==2) opt_2(r);
	}
	return 0;
}

#6284. 数列分块入门 8

区间推平,区间颜色查询,没写分块,珂朵莉树水过了(😓)

#6285. 数列分块入门 9

静态区间最小众数双倍经验P4168 [Violet] 蒲公英

只会88pts,最后几个卡不过去qmq

维护一个离散化,用map<int,int>记录数的出现次数(桶)

维护一个s数组,s[i][j]表示前i个块中数字j出现了几次(前缀和)

维护p数组, p[i][j]第[i,j]块中最小的众数

最后整块有p数组直接找,散块用s数组暴力查

88pts

#include<bits/stdc++.h>
using namespace std;
const int M=320;
inline int read(){
	int sum=0,k=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
	}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
	}return sum*k;
}
inline void wr(int x){
	if(x<0) putchar('-'),x=-1;
	if(x>9) wr(x/10);
	putchar(x%10+'0');
}
struct fenkuai{
	int lz,rz;
}fk[M*M];
int n,bl,m,a[M*M],pos[M*M],p[M][M],s[M][M*M];
vector<int>Li;//离散化~ 
map<int,int>mapp;//记录数字出现次数 
inline int ge(int x){//离散得值 
	return lower_bound(Li.begin(),Li.end(),x)-Li.begin()+1;
}
inline int ask(int l,int r){//l~r的区间最小众数 
	mapp.clear();//清空 
	int ql=pos[l],qr=pos[r],res=0,maxx=-1;//l,r属于的分块 
	if(ql==qr){//l,r在同一块中 
		for(int i=l;i<=r;i++){//暴力找 
			int val=ge(a[i]),num=++mapp[val];
			if((num>maxx)||(num==maxx&&val<res))
				maxx=num,res=val;//更新 
		}
		return Li[res-1];
	}
	for(int i=l;i<=fk[ql].rz;i++) mapp[ge(a[i])]++;//散块 
	for(int i=fk[qr].lz;i<=r;i++) mapp[ge(a[i])]++;//散块 
	res=p[ql+1][qr-1],maxx=mapp[res]+s[qr-1][res]-s[ql][res];//整块的众数 
	for(auto sur:mapp){//遍历散块可能众数 
		int val=sur.first,num=sur.second,ch=num+s[qr-1][val]-s[ql][val];
		if((ch>maxx)||(ch==maxx&&val<res))
			maxx=ch,res=val;//更新~ 
	}return Li[res-1];
}
signed main(){
	n=read();bl=sqrt(n);
	for(int i=1;i<=n;i++) a[i]=read(),Li.push_back(a[i]);
	for(int i=1;i<=bl;i++) fk[i].lz=fk[i-1].rz+1,fk[i].rz=i*bl;
	fk[bl].rz=n;
	for(int i=1;i<=bl;i++) for(int j=fk[i].lz;j<=fk[i].rz;j++) pos[j]=i;
	sort(Li.begin(),Li.end());
	Li.erase(std::unique(Li.begin(),Li.end()),Li.end());
	m=Li.size();
	for(int i=1;i<=bl;i++){
		mapp.clear();
		int res,maxx=-1;
		for(int j=i;j<=bl;j++){
			for(int k=fk[j].lz;k<=fk[j].rz;k++){
				int val=ge(a[k]);mapp[val]++;
				if((mapp[val]>maxx)||(mapp[val]==maxx&&val<res))
					res=val,maxx=mapp[val];
			}
			p[i][j]=res;
		}
	}
	mapp.clear();
	for(int i=1;i<=bl;i++){
		for(int j=fk[i].lz;j<=fk[i].rz;j++) mapp[ge(a[j])]++;
		for(int j=1;j<=m;j++) s[i][j]=mapp[j];
	}
	for(int i=1;i<=n;i++){
		int l=read(),r=read();
		wr(ask(l,r));putchar('\n');
	}
	return 0;
}

すべての生命よ,歌のように輝いています||截剣式、斬、断、破です!||雲璃猫猫が好きですqwq

posted @ 2025-06-20 11:33  rerecloud  阅读(33)  评论(1)    收藏  举报