分塊算法
----------------------------------------------------------
1.思想
如果我們需要對一個特定的序列進行操作,那么非常直觀、簡單的方法就是純暴力(不,那叫模擬)。
不過如果暴力能過的話,那就呵呵了。
所以我們要想一些比較高能的數據結構——分塊。
相比線段樹來說,分塊算法比較難實現,但是只要深入理解,就可以實現了,只不過需要一些數據結構的輔助。
分塊實質來說就是把一個序列切分,從而實現對查詢、查找、替換等等操作的高效處理。
----------------------------------------------------------
2.輔助結構
我們知道,數組的單點查詢時間為O(1),而插入為O(n)
鏈表的單點查詢時間為O(n),而插入為O(1)
而在NOIP里,vector總體來說是比數組快的,所以我們可以用vector來儲存每塊,只不過如果讓插入也這用vector的話,那么時間就不可理喻了。
插入的話,我們可以使用pair
通常要定義這兩個數據結構,簡單的話那個pair就不用了,這要根據題意定義。
----------------------------------------------------------
3.模版
操作:建塊(rebuild)
注意:一開始不必要建塊,但是要在過程中將序列分塊
用途:當當前塊太大時,可以使用此函數將一個大塊分裂
void rebuild() { top=0; for(int i=1;i<=m;i++) { for(vector<int>::iterator j=ve[i].begin();j!=ve[i].end();j++) st[++top]=*j; ve[i].clear();//清空當前塊 } int blo2=sqrt(top); for(int i=1;i<=top;i++) ve[(i-1)/blo2+1].push_back(st[i]); m=(top-1)/blo2+1; }
操作:區間加法(add)
注意:blo是分塊一個重要的變量,是N的開方,大家可以仔細想一想為什么
用途:在需要操作區間加法時可以使用
void add(int a,int b,int c) { for(int i=a;i<=min(bl[a]*blo,b);i++) v[i]+=c,sum[bl[a]]+=c;; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) v[i]+=c,sum[bl[b]]+=c; for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; }
操作:插入
注意:自己可以控制塊的大小
用途:插入元素
void insert(int a,int b) { pair<int,int> t=query(a); ve[t.first].insert(ve[t.first].begin()+t.second,b); if(ve[t.first].size()>20*blo)//分裂大塊 rebuild(); }
常用的分塊只有這幾個操作,更多操作可見hzwer