将一个长度为\(n\)的序列分为若干块,每一个块长度为\(m\),则共有\(n/m\)块。
整块:区间操做时完整地包含于区间的块。
不完整的块:在一个区间操做时,只有部分包含于区间的块,即区间左右端点所在的两个块。
对于整块,咱们能够直接\(O(1)\)的打标记,最后查询时只须要将原来的值加上标记值便可。
对于不完整的块,由于数量较少,直接暴力修改。
这样每次操做的复杂度是\(O(n/m)+O(m)\),根据均值不等式,当\(m\)取\(\sqrt{n}\)时总复杂度最低,为了方便,咱们都默认下文的分块大小为\(\sqrt{n}\)。git
#include <cstdio> #include <cctype> #include <cmath> int tag[50010], a[50010], blo[50010], n, s;//tag为标记,a为原值,blo记录每一个点属于哪一个块 inline int min(int a, int b) { return a < b ? a : b; } inline int read() { int s = 1, w = 0; char ch = getchar(); for(; ! isdigit(ch); ch = getchar()) if(ch == '-') s = -1; for(; isdigit(ch); ch = getchar()) w = w * 10 + ch - '0'; return s * w; } inline void modify(int l, int r, int c) { for(int i = l; i <= min(r, s * blo[l]); i ++) a[i] += c;//左端点所在的不完整的块暴力修改 if(blo[l] != blo[r]) //若是l和r不在同一块则暴力修改,不然在上一步已经修改完成 for(int i = (blo[r] - 1) * s + 1; i <= r; i ++) //暴力修改右端点所在的不完整块 a[i] += c; for(int i = blo[l] + 1; i <= blo[r] - 1; i ++)//O(1)整块打标记 tag[i] += c; } int main() { n = read(), s = sqrt(n); for(int i = 1; i <= n; i ++) a[i] = read(); for(int i = 1; i <= n; i ++) blo[i] = (i - 1) / s + 1;//记录每一个点属于哪一个块 for(int i = 1; i <= n; i ++) { int opt = read(), l = read(), r = read(), c = read(); if(opt == 0) modify(l, r, c);//区间修改 else printf("%d\n", a[r] + tag[blo[r]]);//单点查询 } return 0; }