1、前言html
對線段樹還挺熟悉的我以前卻從沒寫過度塊的題(?)。而後興致一來就決定搜點練習,找到了來自hzwer的《數列分塊入門1-9》,以爲挺不錯的,因而決定作作。c++
2、概念 / 做用算法
概念:將數列等分爲若干個不相交的區間,每個區間稱爲一個塊。數組
做用:優化算法,下降複雜度。具體如何下降,在下面的題目中會逐步說起。題目呈難度遞增趨勢。數據結構
3、題目 / 代碼優化
一、分塊入門1(傳送門:https://loj.ac/problem/6277)spa
題面:給出一個長爲 n 的數列,以及 n 個操做,操做涉及區間加法,單點查詢。code
挺多數據結構均能實現的經典題目,譬如線段樹。這裏咱們用分塊來作。將 n 個元素等分爲若干塊,好比{1, 4, 8, 2, 9, 6, 3, 7, 5},等分爲3塊,則第一塊包含的數據爲{1, 4, 8},第2、三塊以此類推。咱們給每個塊增長一個加法標記,對於每次的區間[l, r]加法操做,直接對塊進行標記疊加。htm
l, r必然不必定是塊的邊界,也就意味着左右端點可能在塊的中間,直接一個個暴力增長。設塊的元素個數爲m,標記塊個數至多n / m個,暴力增長元素個數至多2m個,複雜度分析:O(n / m) + O(m),根據均值不等式,可證實m = √n時存在最低複雜度。故咱們如下全部分塊大小均默認爲√n。blog
詢問就很輕鬆了,直接返回元素的值加上所在區間的標記。
代碼:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 50005 5 6 int n, a[MAXN], x, b[MAXN], f[MAXN]; 7 8 void add(int l, int r, int w) { 9 for (int i = l; i <= min(b[l] * x, r); i++) a[i] += w; 10 if (b[l] != b[r]) 11 for (int i = (b[r] - 1) * x + 1; i <= r; i++) a[i] += w; 12 for (int i = b[l] + 1; i <= b[r] - 1; i++) f[i] += w; 13 } 14 15 int main() { 16 int o, l, r, w; 17 scanf("%d", &n), x = sqrt(n); 18 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 19 for (int i = 1; i <= n; i++) b[i] = (i - 1) / x + 1; 20 for (int i = 1; i <= n; i++) { 21 scanf("%d %d %d %d", &o, &l, &r, &w); 22 if (o) printf("%d\n", a[r] + f[b[r]]); 23 else add(l, r, w); 24 } 25 return 0; 26 }
二、分塊入門2(傳送門:https://loj.ac/problem/6278)
題面:給出一個長爲 n 的數列,以及 n 個操做,操做涉及區間加法,詢問區間內小於某個值 x 的元素個數。
// 對於題目自己的分析就很少贅述了,由於hzwer已經分析的太好了,不在關公們面前耍刀了,因此須要具體的作題思路可轉至hzwer的博客(見上)。
hzwer原代碼使用了vector,正好去了解了下vector(http://www.javashuo.com/article/p-uybbyqbr-kx.html)。固然此題不用vector問題也不大,這裏把兩個版本都貼出來了。
不管是用普通數組仍是vector,有一個須要注意的點是,對於每次的區間修改,左右端點所在的塊會因修改而再也不呈升序排列,因此須要從新維護。
代碼 - 普通版:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 50005 5 6 int n, x; 7 int a[MAXN], b[MAXN], f[MAXN], c[505][505]; 8 9 void reset(int o) { 10 memset(c[o], 0, sizeof(c[o])); 11 for (int i = (o - 1) * x + 1; i <= min(o * x, n); i++) 12 c[o][++c[o][0]] = a[i]; 13 sort(c[o] + 1, c[o] + c[o][0] + 1); 14 } 15 16 void add(int l, int r, int w) { 17 for (int i = l; i <= min(b[l] * x, r); i++) a[i] += w; 18 reset(b[l]); 19 if (b[l] != b[r]) { 20 for (int i = (b[r] - 1) * x + 1; i <= r; i++) a[i] += w; 21 reset(b[r]); 22 } 23 for (int i = b[l] + 1; i <= b[r] - 1; i++) f[i] += w; 24 } 25 26 int query(int l, int r, int w) { 27 int ans = 0; 28 for (int i = l; i <= min(b[l] * x, r); i++) 29 if (a[i] + f[b[l]] < w) ans++; 30 if (b[l] != b[r]) 31 for (int i = (b[r] - 1) * x + 1; i <= r; i++) 32 if (a[i] + f[b[r]] < w) ans++; 33 for (int i = b[l] + 1; i <= b[r] - 1; i++) { 34 int x = w - f[i]; 35 for (int j = 1; j <= c[i][0]; j++) 36 if (c[i][j] < x) ans++; 37 else break; 38 } 39 return ans; 40 } 41 42 int main() { 43 int o, l, r, w; 44 scanf("%d", &n), x = sqrt(n); 45 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 46 for (int i = 1; i <= n; i++) 47 b[i] = (i - 1) / x + 1, c[b[i]][++c[b[i]][0]] = a[i]; 48 for (int i = 1; i <= b[n]; i++) sort(c[i] + 1, c[i] + c[i][0] + 1); 49 for (int i = 1; i <= n; i++) { 50 scanf("%d %d %d %d", &o, &l, &r, &w); 51 if (!o) add(l, r, w); 52 else printf("%d\n", query(l, r, w * w)); 53 } 54 return 0; 55 }
代碼 - vector版:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 50005 5 6 int n, x; 7 int a[MAXN], b[MAXN], f[MAXN]; 8 9 vector <int> v[505]; 10 11 void reset(int o) { 12 v[o].clear(); 13 for (int i = (o - 1) * x + 1; i <= min(o * x, n); i++) v[o].push_back(a[i]); 14 sort(v[o].begin(), v[o].end()); 15 } 16 17 void add(int l, int r, int w) { 18 for (int i = l; i <= min(b[l] * x, r); i++) a[i] += w; 19 reset(b[l]); 20 if (b[l] != b[r]) { 21 for (int i = (b[r] - 1) * x + 1; i <= r; i++) a[i] += w; 22 reset(b[r]); 23 } 24 for (int i = b[l] + 1; i <= b[r] - 1; i++) f[i] += w; 25 } 26 27 int query(int l, int r, int w) { 28 int ans = 0; 29 for (int i = l; i <= min(b[l] * x, r); i++) 30 if (a[i] + f[b[l]] < w) ans++; 31 if (b[l] != b[r]) 32 for (int i = (b[r] - 1) * x + 1; i <= r; i++) 33 if (a[i] + f[b[r]] < w) ans++; 34 for (int i = b[l] + 1; i <= b[r] - 1; i++) { 35 int x = w - f[i]; 36 ans += lower_bound(v[i].begin(), v[i].end(), x) - v[i].begin(); 37 } 38 return ans; 39 } 40 41 int main() { 42 int o, l, r, w; 43 scanf("%d", &n), x = sqrt(n); 44 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 45 for (int i = 1; i <= n; i++) 46 b[i] = (i - 1) / x + 1, v[b[i]].push_back(a[i]); 47 for (int i = 1; i <= b[n]; i++) sort(v[i].begin(), v[i].end()); 48 for (int i = 1; i <= n; i++) { 49 scanf("%d %d %d %d", &o, &l, &r, &w); 50 if (!o) add(l, r, w); 51 else printf("%d\n", query(l, r, w * w)); 52 } 53 return 0; 54 }
To be continued...