分塊算法
----------------------------------------------------------
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
