將一個長度爲\(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; }