[Loj] 数列分块
Posted xayata
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Loj] 数列分块相关的知识,希望对你有一定的参考价值。
数列分块入门 1
区间加 + 单点查询
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int N = 5e4 + 10; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < ‘0‘ || c > ‘9‘) c = gc; while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc; return x; } int A[N], Add[N], bel[N]; int n, block, cnt; void Sec_G(int x, int y, int w) { if(bel[x] == bel[y]) for(int i = x; i <= y; i ++) A[i] += w; else { for(int i = x; i <= bel[x] * block; i ++) A[i] += w; for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w; } for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w; } int main() { n = read(); block = sqrt(n); for(int i = 1; i <= n; i ++) A[i] = read(); for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1; if(n % block) cnt = n / block + 1; else cnt = n / block; int T = n; while(T --) { int opt = read(), l = read(), r = read(), c = read(); if(!opt) Sec_G(l, r, c); else cout << A[r] + Add[bel[r]] << endl; } return 0; }
数列分块入门 2
区间加法,询问区间内小于某个值 x 的元素个数
用B[]记录A[], B[]数组中为排好序的A[]的映射
那么每次可以对每一块进行二分查找
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; const int N = 5e4 + 10; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < ‘0‘ || c > ‘9‘) c = gc; while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc; return x; } int A[N], B[N], Add[N], bel[N]; int n, block, cnt; void Work_sort(int x) { int l = (x - 1) * block + 1, r = min(l + block - 1, n); for(int i = l; i <= r; i ++) B[i] = A[i]; sort(B + l, B + r + 1); } void Sec_G(int x, int y, int w) { if(bel[x] == bel[y]) { for(int i = x; i <= y; i ++) A[i] += w; Work_sort(bel[x]); } else { for(int i = x; i <= bel[x] * block; i ++) A[i] += w; Work_sort(bel[x]); for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w; Work_sort(bel[y]); } for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w; } inline int Calc(int x, int w) { int l = (x - 1) * block + 1, r = min(l + block - 1, n), ret = 0; while(l <= r) { int mid = (l + r) >> 1; if(B[mid] + Add[x] < w) ret = mid, l = mid + 1; else r = mid - 1; } return ret ? (ret - (x - 1) * block) : 0; } inline int Sec_A(int x, int y, int w) { int ret(0); if(bel[x] == bel[y]) { for(int i = x; i <= y; i ++) if(A[i] + Add[bel[x]] < w) ret ++; return ret; } else { for(int i = x; i <= bel[x] * block; i ++) if(A[i] + Add[bel[x]] < w) ret ++; for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) if(A[i] + Add[bel[y]] < w) ret ++; } for(int i = bel[x] + 1; i < bel[y]; i ++) ret += Calc(i, w); return ret; } int main() { n = read(); block = sqrt(n); for(int i = 1; i <= n; i ++) A[i] = read(); for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1; if(n % block) cnt = n / block + 1; else cnt = n / block; for(int i = 1; i <= cnt; i ++) Work_sort(i); int T = n; while(T --) { int opt = read(), l = read(), r = read(), c = read(); if(!opt) Sec_G(l, r, c); else cout << Sec_A(l, r, c * c) << "\n"; } return 0; }
数列分块入门 3
区间加法,询问区间内小于某个值 x 的前驱
与2类似,二分查找
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; const int N = 1e5 + 10; const int oo = 999999999; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < ‘0‘ || c > ‘9‘) c = gc; while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc; return x; } int A[N], B[N] = {-1}, Add[N], bel[N]; int n, block, cnt; void Work_sort(int x) { int l = (x - 1) * block + 1, r = min(l + block - 1, n); for(int i = l; i <= r; i ++) B[i] = A[i]; sort(B + l, B + r + 1); } void Sec_G(int x, int y, int w) { if(bel[x] == bel[y]) { for(int i = x; i <= y; i ++) A[i] += w; Work_sort(bel[x]); } else { for(int i = x; i <= bel[x] * block; i ++) A[i] += w; Work_sort(bel[x]); for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w; Work_sort(bel[y]); } for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w; } inline int Calc(int x, int w) { int l = (x - 1) * block + 1, r = min(l + block - 1, n), ret = 0; while(l <= r) { int mid = (l + r) >> 1; if(B[mid] + Add[x] < w) ret = mid, l = mid + 1; else r = mid - 1; } return B[ret] + Add[x]; } inline int Sec_A(int x, int y, int w) { int ret = -1; if(bel[x] == bel[y]) { for(int i = x; i <= y; i ++) if(A[i] + Add[bel[x]] < w && A[i] + Add[bel[x]] > ret) ret = A[i] + Add[bel[x]]; return ret; } else { for(int i = x; i <= bel[x] * block; i ++) if(A[i] + Add[bel[x]] < w && A[i] + Add[bel[x]] > ret) ret = A[i] + Add[bel[x]]; for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) if(A[i] + Add[bel[y]] < w && A[i] + Add[bel[y]] > ret) ret = A[i] + Add[bel[y]]; } for(int i = bel[x] + 1; i < bel[y]; i ++) { int imp = Calc(i, w); if(imp < w && imp > ret) ret = imp; } return ret; } int main() { n = read(); block = sqrt(n); for(int i = 1; i <= n; i ++) A[i] = read(); for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1; if(n % block) cnt = n / block + 1; else cnt = n / block; for(int i = 1; i <= cnt; i ++) Work_sort(i); int T = n; while(T --) { int opt = read(), l = read(), r = read(), c = read(); if(!opt) Sec_G(l, r, c); else cout << Sec_A(l, r, c) << "\n"; } return 0; }
数列分块入门 4
区间加法,区间求和
没什么好说的
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int N = 5e4 + 10; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < ‘0‘ || c > ‘9‘) c = gc; while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc; return x; } #define LL long long LL A[N], Add[N], bel[N], W[N]; int n, block, cnt, Mod; void Sec_G(int x, int y, int w) { if(bel[x] == bel[y]) for(int i = x; i <= y; i ++) A[i] += w, W[bel[x]] += w; else { for(int i = x; i <= bel[x] * block; i ++) A[i] += w, W[bel[x]] += w; for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w, W[bel[y]] += w; } for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w, W[i] += w * block; } inline int Sec_A(int x, int y) { LL ret = 0; if(bel[x] == bel[y]) for(int i = x; i <= y; i ++) ret += (A[i] + Add[bel[x]]) % Mod; else { for(int i = x; i <= bel[x] * block; i ++) ret += (A[i] + Add[bel[x]]) % Mod; for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) ret += (A[i] + Add[bel[y]]) % Mod; } for(int i = bel[x] + 1; i < bel[y]; i ++) ret += W[i] % Mod; return ret% Mod; } int main() { n = read(); block = sqrt(n); for(int i = 1; i <= n; i ++) A[i] = read(); for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, W[bel[i]] += A[i]; if(n % block) cnt = n / block + 1; else cnt = n / block; int T = n; while(T --) { int opt = read(), l = read(), r = read(), c = read(); Mod = c + 1; if(!opt) Sec_G(l, r, c); else cout << Sec_A(l, r) << endl; } return 0; }
数列分块入门 5
区间开方,区间求和
一个数(合理)开几次根后就是0/1了
因此,只需记录每块的最大值,如果最大值是0/1就没必要开根
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int N = 5e4 + 10; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < ‘0‘ || c > ‘9‘) c = gc; while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc; return x; } #define LL long long LL A[N], Add[N], bel[N], W[N], Max[N]; int n, block, cnt, Mod; inline void Sec_G(int x, int y) { if(bel[x] == bel[y]) { if(!Max[bel[x]]) return ; if(Max[bel[x]] == 1) return ; for(int i = x; i <= y; i ++) { int C = A[i] - (int) sqrt(A[i]); W[bel[x]] -= C; A[i] = (int) sqrt(A[i]); } LL Max_A = 0; for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) Max_A = max(Max_A, A[i]); Max[bel[x]] = Max_A; return ; } else { if(Max[bel[x]] && Max[bel[x]] != 1) { for(int i = x; i <= bel[x] * block; i ++) { int C = A[i] - (int) sqrt(A[i]); W[bel[x]] -= C; A[i] = (int) sqrt(A[i]); } LL Max_A = 0; for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) Max_A = max(Max_A, A[i]); Max[bel[x]] = Max_A; } if(Max[bel[y]] && Max[bel[y]] != 1) { for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) { int C = A[i] - (int) sqrt(A[i]); W[bel[y]] -= C; A[i] = (int) sqrt(A[i]); } LL Max_A = 0; for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) Max_A = max(Max_A, A[i]); Max[bel[y]] = Max_A; } } for(int i = bel[x] + 1; i < bel[y]; i ++) { if(!Max[i] || Max[i] == 1) continue ; LL Max_A = 0; for(int j = (i - 1) * block + 1; j <= i * block; j ++) { int C = A[j] - (int) sqrt(A[j]); W[i] -= C; A[j] = (int) sqrt(A[j]); Max_A = max(Max_A, A[j]); } Max[i] = Max_A; } } inline int Sec_A(int x, int y) { LL ret = 0; if(bel[x] == bel[y] && Max[bel[x]]) for(int i = x; i <= y; i ++) ret += A[i]; else { for(int i = x; i <= bel[x] * block && Max[bel[x]]; i ++) ret += A[i]; for(int i = (bel[y] - 1) * block + 1; i <= y && Max[bel[y]]; i ++) ret += A[i]; } for(int i = bel[x] + 1; i < bel[y]; i ++) ret += W[i]; return ret; } int main() { n = read(); block = sqrt(n); for(int i = 1; i <= n; i ++) A[i] = read(); for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, W[bel[i]] += A[i], Max[bel[i]] = max(Max[bel[i]], A[i]); int T = n; while(T --) { int opt = read(), l = read(), r = read(), c = read(); if(!opt) Sec_G(l, r); else cout << Sec_A(l, r) << "\n"; } return 0; }
数列分块入门 6
单点插入,单点询问
数据随机,分块,对于每一块开动态数组,插入 + 查询比较容易实现
如果数据不随机,就有可能加到同一块中的数较多,影响效率
这样可以进行一定的插入操作之后重新分块
#include <iostream> #include <cstdio> #include <cmath> #include <vector> using namespace std; const int N = 1e5 + 10; int A[N << 1], n; vector <int> Vec[350]; int block, bel[N]; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < ‘0‘ || c > ‘9‘) c = gc; while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc; return x; } inline void Ins(int x, int a) { int now_size(0), Whi; for(int i = 1; ; i ++) { int Size = Vec[i].size(); now_size += Size; if(now_size >= x) { Whi = i; x -= (now_size - Size); break; } } Vec[Whi].insert(Vec[Whi].begin() + x - 1, a); } inline int Poi_A(int x) { int Whi_, now_size(0); for(int i = 1; ; i ++) { int Size = Vec[i].size(); now_size += Size; if(now_size >= x) { int iii = x - (now_size - Size); return Vec[i][iii - 1]; } } } int main() { n = read(); block = sqrt(n); for(int i = 1; i <= n; i ++) A[i] = read(); for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1; for(int i = 1; i <= n; i ++) Vec[bel[i]].push_back(A[i]); int T = n; while(T --) { int opt = read(), l = read(), r = read(), c = read(); if(!opt) Ins(l, r); else cout << Poi_A(r) << endl; } return 0; }
数列分块入门 7
区间乘法,区间加法,单点询问
先乘后加,乘的时候相应的加法标记也要乘
#include <iostream> #include <cstdio> #include <cmath> #include <vector> using namespace std; const int N = 1e5 + 10; const int Mod = 1e4 + 7; #define LL long long LL A[N], Mul[N], Add[N], bel[N]; int n, cnt, block; #define gc getchar() inline int read() { int x = 0, f = 1; char c = gc; while(c < ‘0‘ || c > ‘9‘) {if(c == ‘-‘) f = -1; c = gc;} while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc; return x; } inline void Sec_Add(int x, int y, int w) { if(bel[x] == bel[y]) { for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[x]] + Add[bel[x]]); Add[bel[x]] = 0; Mul[bel[x]] = 1; for(int i = x; i <= y; i ++) A[i] += w, A[i] %= Mod; return ; } else { for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod; Add[bel[x]] = 0; Mul[bel[x]] = 1; for(int i = x; i <= bel[x] * block; i ++) A[i] += w, A[i] %= Mod; for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod; Add[bel[y]] = 0; Mul[bel[y]] = 1; for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w, A[i] %= Mod; } for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w, Add[i] %= Mod; } inline void Sec_Mul(int x, int y, int w) { if(bel[x] == bel[y]) { for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod; Add[bel[x]] = 0; Mul[bel[x]] = 1; for(int i = x; i <= y; i ++) A[i] = (A[i] * w) % Mod; return ; } else { for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod; Add[bel[x]] = 0; Mul[bel[x]] = 1; for(int i = x; i <= bel[x] * block; i ++) A[i] = (A[i] * w) % Mod; for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod; Add[bel[y]] = 0; Mul[bel[y]] = 1; for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] = (A[i] * w) % Mod; } for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] = (Add[i] * w) % Mod, Mul[i] = (Mul[i] * w) % Mod; } int main() { n = read(); for(int i = 1; i <= n; i ++) A[i] = read(); block = sqrt(n); for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, Mul[i] = 1; int T = n; while(T --) { int opt = read(), l = read(), r = read(), c = read(); if(opt == 0) Sec_Add(l, r, c); else if(opt == 1) Sec_Mul(l, r, c); else cout << (A[r] * Mul[bel[r]] + Add[bel[r]]) % Mod << "\n"; } return 0; }
数列分块入门 8
数列分块入门 9
以上是关于[Loj] 数列分块的主要内容,如果未能解决你的问题,请参考以下文章