分块一览

前言

如题。

值域分块

顾名思义,就是在桶上分块。

它的用处是把区间修改和区间询问中某一种操作变成 \(O(1)\),另一种变成 \(O(\sqrt n)\)

所以经常用来辅助维护两种操作数量严重不对等的数据结构。

典型代表有莫队和根号分治。

这里看一个莫队的例子。

如我们要维护一个二维数点。

那么莫队上的操作就是 \(O(n \sqrt n)\) 次单点加和 \(O(n)\) 次区间求和。

那么单点加时维护所属块的总和,就可以在区间查询时加速对于整块的查询而做到 \(O(\sqrt n)\) 查询。

具体代码如下:

void Add(int x){
	int bl=x/sq+1;
	if(x%sq==0){
		bl--;
	}
	cnt[x]++;
	block[bl]++;
}
void Del(int x){
	int bl=x/sq+1;
    if(x%sq==0){
		bl--;
	}
	cnt[x]--;
	block[bl]--;
}
int query(int x){
    int res=0;
	for(int i=0;i<=400;i++){
		if((i-1)*sq+1<=x&&x<=i*sq){
			for(int j=(i-1)*sq+1;j<=i*sq;j++){
                res+=cnt[j];
				if(j==x){
					return res;
				}
			}
		}
        res+=block[i];
	}
    return res;
}

根号重构

这个方法是用来维护诸如翻转之类的区间操作的。

首先你需要把所有点缩到一个块里。

然后对于每次操作,把操作两个端点从其所属块中断开(类似于珂朵莉树的搞法)。

然后这个问题就变成了整块下的问题。

那么现在就可以暴力去搞了。

但是注意到块很多的时候时间会退化到 \(O(n^2)\)

所以当块的数量多余 \(\sqrt n\) 时,暴力重构所有块。

具体来说把所有块再次缩到一个块中。

这样保证了操作复杂度不会高于 \(O(\sqrt n)\)

另外由于每次只会增加常数个块,所以重构数量是 \(O(\sqrt n)\) 的。

那么总复杂度也就只有 \(O(n \sqrt n)\)

莫队二离

posted @ 2024-02-27 18:13  ChiFAN鸭  阅读(16)  评论(0编辑  收藏  举报