分塊演算法學習筆記

2022-09-20 07:36:09 字數 3209 閱讀 9027

分塊簡介

分塊的基本思想是通過對原資料的適當劃分,並在劃分後的每乙個塊上預處理部分資訊,從而較一般的暴力演算法取得更優的時間複雜度。

如何分塊

一般的,我們會把原陣列分成塊長為 \(\sqrt\) 的幾段,初始化的複雜度為 \(o(n)\) ,單次操作的複雜度是 \(o(\sqrt)\)​

p3372 【模板】線段樹 1

參考題目可以發現,出題人貌似想讓我們寫線段樹,於是我們考慮如何不寫線段樹,我們可以採用分塊。

關於變數的一些定義:

\(block\)​ :塊長

\(tot\) :總塊數

\(l_i\) :第 \(i\) 塊的左端點

\(r_i\) :第 \(i\) 塊的右端點

\(pos_i\) :第 \(i\) 個數在所在的塊的編號

\(s_i\) :第 \(i\) 塊所有數字的和

\(tag_i\)​ :第 \(i\) 塊的加法標記,具體作用見下文

初始化:

首先我們定義塊長為 \(\sqrt\)

接下來列舉這個區塊內的所有元素,用 \(s_i\) 記錄總和,同時把這些元素的 \(pos\) 標記為這個塊的編號

因為我們只遍歷了一整個序列,所以預處理的複雜度是 \(o(n)\) 的

初始化部分**

inline void build() 

}

區間加法

我們要將 \(a_x \sim a_y\) 加上 \(val\) ,暴力複雜度顯然是 \(o(n)\)​ ,考慮優化

如果 \(x,y\) 在同乙個塊,顯然暴力加的複雜度不會超過 \(o(\sqrt)\) ,直接暴力即可

如果 \(x,y\)​​​​ 不在同乙個塊,我們可以把這個塊分成兩邊的零散塊與中間的整塊分別處理

旁邊的零散塊直接暴力處理

對於中間的整塊,更新每乙個加法標記 \(tag\) ,同時更新這個塊的和 \(s\) 即可

由於塊長為 \(o(\sqrt)\) ,零散塊的操作複雜度不會超過 \(o(2 \times \sqrt)\) ,整塊的數量不會超過 \(\sqrt\)​

所以這一部分的複雜度為 \(o(sqrt)\)​

區間加法**:

inline void plus(int x,int y,int val) 

else

}

區間查詢

查詢 \(\sum^_a_i\)​

如果 \(x,y\) 在同乙個塊,顯然暴力查詢的複雜度不會超過 \(o(\sqrt)\)​​ ,注意不要忘記加上加法標記

如果 \(x,y\)​ 不在同乙個塊,我們兩邊的零散塊同樣暴力處理,中間的整塊直接累計和即可

同樣,這一部分的時間複雜度為 \(o(\sqrt)\)

區間查詢**:

inline int query(int x,int y) 

else

return ans;

}

將以上**結合起來,我們就可以用分塊 ac 本題

完整**:

#include #include #define min(a,b) ((a)<(b)?(a):(b))

typedef long long ll;

using namespace std;

const int n=1e5+7;

ll a[n];

ll s[n],tag[n];

int l[n],r[n],pos[n];

int n,m,block,tot;

inline void build()

}inline void plus(int x,int y,ll val)

else

}inline ll query(int x,int y)

else

return ans;

}signed main()

else

printf("%lld\n",query(l,r));

} return 0;

}

習題

p2357 守墓人

與例題思想相近,稍加修改即可ac

p3870 [tjoi2009]開關

p5057 [cqoi2006]簡單題

p2846 [usaco08nov]light switching g

sp7259 lite - light switching

p2574 xor的藝術

區間異或,將模板稍微改一下就行,還可以收穫5倍經驗

p4145 上帝造題的七分鐘 2 / 花神遊歷各國

區間開方與求和

求和直接用分塊即可,那麼怎麼進行區間開方呢

注意到乙個性質,乙個數一直開方後總會變成 \(1\) 或 \(0\)

所以我們一開始先暴力開方,如果一段區間內所有的數都是 \(1\) 或 \(0\) ,那麼這段區間不管怎麼開方,它的每乙個數都不會被改變,所以在修改時這段區間直接跳過就行

**:

#include #include #define max(a,b) ((a)>(b)?(a):(b))

#define min(a,b) ((a)<(b)?(a):(b))

typedef long long ll;

using namespace std;

const int n=1e5+7;

ll a[n];

ll s[n];

int l[n],r[n],pos[n];

bool vis[n];

int n,m;

int block,tot;

inline void build()

}inline void check(ll x)

inline void update(int x,int y)

check(l);

} }else

check(l);

for(int i=l+1;iif(!vis[i])

check(i);

}for(int i=l[r];i<=y;++i)

check(r); }}

inline ll query(int x,int y)

else

return ans;

}signed main()

return 0;

}

演算法學習 分塊演算法入門

完整 如何寫分塊 二 所謂分塊演算法,就是講乙個序列分成若干塊,維護塊內的資訊。為了保證一定的時間複雜度,所以對於乙個 n n 個元素組成的陣列,將其分為 n role presentation n n塊,每塊也有n n 個元素。所以一般分塊演算法的複雜中都帶有根號。對於乙個暴力的區間修改問題,方法...

演算法 學習筆記

1.輸入輸出演算法至少有乙個或多個輸出 2.有窮性 3.確定性 4.可行性 1.正確性a.演算法程式沒有語法錯誤 b.演算法程式對於合法的輸入資料能夠產生滿足要求的輸出結果 c.演算法程式對於非法的輸入資料能夠得出滿足規格說明的結果 d.演算法對於精心選擇的,甚至刁難的測試資料都有滿足要求的輸出結果...

演算法學習筆記

複雜度分析 1.只關注迴圈次數最多的一行 2.總複雜度等於量級最大 的複雜度 3.巢狀 的複雜度等於巢狀 內外複雜度的乘積 單鏈表結構和順序儲存結構的優缺點 儲存分配方式 時間效能 空間效能 單鏈表結構 用一組任意的儲存單元存放線性表元素 查詢 o n 插入和刪除 找到某位置的指標後,插入和刪除的時...