分塊簡介
分塊的基本思想是通過對原資料的適當劃分,並在劃分後的每乙個塊上預處理部分資訊,從而較一般的暴力演算法取得更優的時間複雜度。
如何分塊
一般的,我們會把原陣列分成塊長為 \(\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 插入和刪除 找到某位置的指標後,插入和刪除的時...