分桶法:给一个n个元素的数组a[0...n],分桶法的思想是将这n个元素进行均分,均分成√n组,每组里有√n个元素,这里“组”的意思与“桶”等价。这种分法也称之“平方分割”。
这样会带来哪些处理上的好处呢?其实类似于线段树,分桶法的思想也是每个桶分别维护各自桶内部的信息,然后在处理区间问题上带来快捷。这里以RMQ(Range Minimum Query)为例:
n个元素的数组a[],我们的任务有两个:
- 给定l,r,我们要答出区间[l,r]上的最小值;
- 给定i,x,我们要将a[i]替换成x.
b代表”桶宽“,取b=[√n], 然后依次在每个桶中放入b个元素:
b=(int) sqrt(n);
vector<int> bucket[MAX_N/b];
for(int i=0;i<n;i++) {
bucket[i/b].push_back(a[i]);
}
val[i]
任务1:查询
- [l,r]完全的包含了桶bucket[i],则直接查询桶bucket[i]所维护的那个值;
- [l,r]与桶bucket[i]有交集但并不完全包含该桶(或者说桶i仅有一部分在[l,r]中),这时我们便遍历该桶在查询区间[l,r]中的那一部分。
[l,r]时,我们需要遍历的元素个数不超过n/b个,这部分的复杂度是O(√n);而询问各个桶维护的最小值所要的复杂度也是O(√n),所以查询时的总的复杂度是O(√n)。
任务2:更新
- 更新元素时,直接遍历一遍更新后的桶中的值,得出最小值即可
x与桶k维护的值val[k]直接比较取min{val[k],x},因为桶不知道替换掉的是谁,可能是val[k]所在的a[i]被x替换了(不过感觉大家不会出这种错啦~
O(√n)。
m,那么总的操作复杂度就是O(m√n),条件不苛刻是还是推荐使用的,毕竟实现起来比线段树简单。
推荐题目:K-th Number(POJ 2104)