13.1 分块思想

第13章 专题扩展
13.1 分块思想
给出一个非负整数序列A,元素个数为N(N<=10^5,A[i]<=10^5),在有可能随时添加或删除原数的情况下,实时查询序列元素第K大,即把序列元素从小到大排序后从左到右的第K个元素。例如对序列{2,7,5,1,6}来说,此时序列第3大为5;之后插入元素4,这样序列第3大就是4;然后删除元素,于是序列第1大就是2.
如果在查询的过程中元素可能发生改变(例如插入、修改或删除),就称这种查询为在线查询;如果在查询过程中元素不发生改变,就称为离线查询。显然,上面的序列元素第K大的问题是在线查询,如果直接暴力做,在添加跟删除元素时就要有O(n)的实践复杂度来移动序列的元素,效率极其低下。事实上,序列元素第K大有很多解决方法,介绍其中较容易理解、写法也很简洁的一种做法,即分块的思想。
从字面意思理解“分块”,就是把有序元素划分为若干块。例如,可以把拥有9个元素的有序序列{1,2,4,9,12,34,56,78,87}分为3块{1,2,4}、{9,12,34}、{56,78,87}。为了达到高效率的目的,对一个有N个元素的有序序列来说,除最后一块外,其余每块中元素的个数都应当为floor(sqrt(N)),于是块数为ceil(sqrt(N))。这样就把有序序列划分为ceil(sqrt(N))块,其中每块中元素的个数不超过floor(sqrt(N))。例如对有9个元素的序列来说,就应当分为sqrt(9)=3块,其中每块中的元素个数为别为3、3、3;而对有11个元素的序列来说,就应当分为ceil(sqrt(11))=4块,其中每块中的元素个数分别为3、3、3、2。

暴力的做法由于添加和删除元素时需要O(n)的复杂度来移动元素,那么如何用分快法降低这个时间呢?考虑到序列中的元素都是不超过10^5的非负整数,因此不妨设置一个hash数组table数组table[100001],其中table[x]表示整数x的当前存在个数;接着,借助分块思想,从逻辑上将0~10^5分为ceil(sqrt(10^5+1))=317块,其中每块的元素个数为floor(sqrt(10^5+1))=316。逻辑上进行分块的结果如下:
0,1,2,……,314,315为第0块;
316,317,……,630,631为第1块。
……
99856,99857,……,100000为第316块。
定义一个统计数组block[317],其中block[i]表示第i块中存在的元素个数。于是加入要新增一个元素x,就可以先计算出x所在的块号为x / 316,然后让block[x/316]加1,表示该块中元素个数多了1;同时令table[x]加1,表示整数x的当前存在个数多了1.

猜你喜欢

转载自www.cnblogs.com/longxue1991/p/12330033.html