[总集] LOJ 分块1 – 9

本文详细介绍了分块算法在解决数列问题中的应用,包括区间加、区间排名、单点插入、区间加乘查询、区间赋值及区间计数等。通过分块思想,可以将复杂度降低,提高算法效率。文章还提到了动态插入、分块重构等优化策略,以应对不同情况下的数据挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分块9题

出题人hzw的解析



(tips.以下代码中IO优化都已省去,想看可以点传送门)


数列分块入门 1

  • 修改:区间加

  • 查询:单点值查询

这是一道经典题目,线段树、树状数组等都可以搞,这里讲讲分块

分块就是将一定长度的一段数打包成块,统一处理的算法

每个块都有自己的信息,自己的标记,统一维护,统一查询

我们可以将每个区间修改或查询拆分成在若干个整块,和头尾两个不完整的块中修改、查询后信息的总和

那此题中块要分多大呢?答案是 n \sqrt n n

由于本人太弱,下面给出hzw大佬的证明:

如果我们把每m个元素分为一块,共有 n m \frac{n}{m} mn块,每次区间加的操作会涉及O( n m \frac{n}{m} mn)个整块,以及区间两侧两个不完整的块中至多2m个元素。
我们给每个块设置一个加法标记(就是记录这个块中元素一起加了多少),每次操作对每个整块直接 O ( 1 ) O(1) O(1)标记,而不完整的块由于元素比较少,暴力修改元素的值。
每次询问时返回元素的值加上其所在块的加法标记。
这样每次操作的复杂度是 O ( n m ) + O ( m ) O(\frac{n}{m})+O(m) O(mn)+O(m),根据均值不等式,当m取 n \sqrt n n 时总复杂度最低

代码

#include <bits/stdc++.h>
#define rint register int
using namespace std;
const int N=50005,B=225; //B是最大块数
int n,len,bn; //bn是块数,len是块长
int L[B],R[B],tag[B],a[N],block[N];
//L[i]是块i的左边界,R[i]是块i的右边界,tag[i]是块i的统一加法标记
//a[i]表示元素i不加标记时的值,block[i]表示元素i的所属块
inline void add(int l,int r,int c){
   
   
	int p=block[l],q=block[r]; //p是左边界的所属块,q是右边界的所属块
	if(p==q){
   
    //如果左右边界在同一块中
		for(int i=l;i<=r;++i) //直接对[l,r]每个元素进行修改
			a[i]+=c;
		return ;
	}
	for(rint i=p+1;i<=q-1;++i)
		tag[i]+=c; //给整块打上加法标记,就可以忽略块中的元素
	for(rint i=l;i<=R[p];++i)
		a[i]+=c; //给左边剩下的元素进行处理
	for(rint i=L[q];i<=r;++i)
		a[i]+=c; //给右边剩下的元素进行处理
}
signed main(){
   
   
	read(n);
	for(rint i=1;i<=n;++i)
		read(a[i]);
	bn=len=sqrt(n);
	for(rint i=1;i<=bn;++i){
   
   
		L[i]=(i-1)*len+1;
		R[i]=i*len;
		for(rint j=L[i];j<=R[i];++j)
			block[j]=i; //给没个元素规定所属块
	}
	if(R[bn]<n){
   
    //如果没分完就再分一个
		L[++bn]=R[bn-1]+1;
		R[bn]=n;
		for(rint i=L[bn];i<=n;++i)
			block[i]=bn;
	}
	for(rint i=1;i<=n;++i){
   
   
		int opt,l,r,c;
		read(opt);
		read(l);
		read(r);
		read(c);
		if(!opt) add(l,r,c); //修改
		else Write(a[r]+tag[block[r]],'\n'); //查询:自己的值加上自己所属块的统一标记
	}
}

数列分块入门 2

  • 修改:区间加

  • 查询:区间排名

这题与1相似,只是需要额外维护一个st[i][j]表示块i从小到大排序后排名第j的数

用sort暴力维护它的单调性有助于使用lower_bound()直接得到某个块中的排名,再将信息合并即可

修改部分只需在1的基础上在每次对一个块完成修改后维护它的st[][]即可

查询部分对于零散小块,暴力枚举判断;对于整块,二分查找确定排名即可得到该块中的答案;最后再将所有块中的答案累加即可

代码

#include <bits/stdc++.h>
#define rint register int
using namespace std;
const int N=50050,B=235;
int n,len,bn,L[B],R[B],tag[B],a[N],block
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值