分块(板子)

“优雅的暴力”——分块

分块是一种暴力的优化,虽然效率比线段树、树状数组等数据结构低的多 \((N+Q)\sqrt{N}\),但是更加灵活。

分块的思想是把整个区间分成几个部分,对于要处理的区间包括两个部分 “整块”,和 区间边缘的“零散块”

“零散块”直接暴力,“整块”进行整体操作即可。

听起来可能和线段树有点像,其实分块可以看成一颗只有三层的树,

区别在于分块可以处理一些区间之间没有结合律的(如众数)。

分块思路相对好想,重点在于如何把整个区间的操作转化为“整块”的操作。

例题


$\mathbb{code}$
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100005;
int n,m;
LL a[N],sum[400],add[400];
int L[400],R[400],bl[N],siz[400];
void bui()
{
	int cnt=sqrt(n);
	for(int i=1;i<=cnt;i++)
	{
		L[i]=n/cnt*(i-1)+1;
		R[i]=n/cnt*i;
	}
	R[cnt]=n;
	
	for(int i=1;i<=cnt;i++)
	{
		for(int j=L[i];j<=R[i];j++)
		{
			bl[j]=i; sum[i]+=a[j];	
		}
		siz[i]=R[i]-L[i]+1;	
	}

}
void mdf(int l,int r,int v)
{
	if(bl[l]==bl[r])
	{
		for(int i=l;i<=r;i++)
		{
			a[i]+=v; sum[bl[l]]+=v;
		}
	}
	else
	{
		for(int i=l;i<=R[bl[l]];i++)
		{
			a[i]+=v; sum[bl[l]]+=v;
		}
		for(int i=L[bl[r]];i<=r;i++)
		{
			a[i]+=v; sum[bl[r]]+=v;
		}
		for(int i=bl[l]+1;i<bl[r];i++)
		{
			add[i]+=v;
		}	
	}
}
LL que(int l,int r)
{
	LL ans=0;
	if(bl[l]==bl[r])
	{
		for(int i=l;i<=r;i++) ans+=a[i]+add[bl[l]];
	}
	else
	{
		for(int i=l;i<=R[bl[l]];i++) ans+=a[i]+add[bl[l]];
		for(int i=L[bl[r]];i<=r;i++) ans+=a[i]+add[bl[r]];
		for(int i=bl[l]+1;i<bl[r];i++) ans+=sum[i]+siz[i]*add[i];
	}
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	bui();
	for(int i=1;i<=m;i++)
	{
		string s; cin>>s;
		if(s[0]=='C')
		{
			int l,r,v;
			scanf("%d%d%d",&l,&r,&v);
			mdf(l,r,v);
		}
		if(s[0]=='Q')
		{
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%lld\n",que(l,r));
		}
	}
	return 0;
}
posted @ 2024-04-22 21:13  ppllxx_9G  阅读(13)  评论(0编辑  收藏  举报