分块(基本思想详解)

本蒟蒻刚刚看懂了分块于是就出来写一篇博客
千灯给你们整理一下分块。
一般来说,看到给一个区间[L,R]增加一定值,或者搜索一个区间那么就可以用树状数组 ,分块做。
树状数组比分块快
分块其实是一种优化过的暴力。
首先我们真的要把这个东东切块
分块(基本思想详解)_第1张图片
↑一般来说分成 n \sqrt n n 块(分不平均没事极力掩盖画图水平差 ),每一块管理 n \sqrt n n 个点,我们定义block(区块)= n \sqrt n n ,每一块管理block个点。
然后开一个数组seat(位置),来确定第i个点在哪个区域(调用起来比较快)。

for(register int i=1;i<=n;++i)
	seat[i]=(i-1)/block+1;//第block个点应该属于第一个区块

然后我们用一个数组value_block记录组的值,用value_drop记录单个点的值
所以对于第i个点的真实值应该是value_block[seat[i]]+value_drop[i]
因为扫[L,R]的区间时会有 : 不完整的区块+完整的区块+不完整的区块
分块(基本思想详解)_第2张图片
比如这样的图↑在区间[L,R]中间,包含了完整的块(绿色),不完整的块(咖啡色)
对于完整的块我们在value_block[ ]上加,最高的复杂度就是全扫一遍 O ( n ) O(\sqrt n) O(n )
对于剩下的不完整的块在value_drop[ ]一个点一个点加,最多就是 O ( 2 × n − 2 ) O(2×\sqrt n-2) O(2×n 2)
复杂度高不到哪里去,但是比树状数组慢好多。
然后讲一下特殊情况(代码中会特殊处理QAQ)↓:
分块(基本思想详解)_第3张图片
那么理论讲完,来看看代码。
对于靠近L的咖啡色块的处理↓:

int end_left=min(seat[L]*block,R);//seat[L]*block,处理出L所在区域的最右边
				 //min中有R是因为特殊情况,这种情况咖啡色部分是不到该区块底的
for(register i=L;i<=end_left;++i)    
	value_drop[i]+=what;

对于靠近R的咖啡色块的处理↓:

int start_right=max((seat[R]-1)*block+1,L)
//与处理L同理,(seat[R]-1)*block+1是R所在区块的第一个点
//max中加入L是怕特殊情况
for(register i=start_right;i<=R;++i)    	
	value_drop[i]+=what;

然后就是对绿色区块的处理↓:

for(register i=seat[L]+1;i<=seat[R]-1;++i)
	value_block[i]+=what;

这大概就是基本理论(区间加法)
虽然没有树状数组快但是很好理解,(而且什么都能维护)。
QAQ有不对的地方请大佬指出

你可能感兴趣的:(分块(基本思想详解))