题目大意:线段树$1$(区间修改,区间查询)
题解:\$分治,($skip1978$发明的一种算法)
(以下引用自$skip1978$的博客,代码风格和其中出错的地方做了修改)
对于一类数据结构问题,修改查询,可以快速计算连续的修改对连续的询问的贡献(也不用多快,看看下面就好),还可以快速计算一个修改对一个询问的贡献,那么就可以使用一种神奇的分治解决
$\$$分治是一种处理一类问题暴力而又有效的问题,这个分治一共分两步($op_i=1$表示修改,$op_i=2$表示询问):
const int Blo = 10; void $(int l, int r) { if(r - l > Blo) { int mid = l + r >> 1; $(l, mid), $(mid + 1, r); clear(); for (int i = l; i <= mid; i++) if (op[i] == 1) modify(i); calc(); for (int i = mid + 1; i <= r; i++) if (op[i] == 2) ans[i] += get(pos[i]); } else { for (int i = l; i <= r; i++) if (op[i] == 1) { for (int j = i + 1; j <= r; j++) if (op[j] == 2) make(i, j); } } }
注意:$分治的是操作
这就是一个\$分治的板子了,下面主要介绍用到的函数的作用
clear()
清空
clear()
计算编号为$x$的修改的贡献
calc()
为查询预处理当前的情况
get(x)
得到查询$x$所受到的贡献
make(x,y)
计算编号为$x$的操作对$y$询问的贡献
如果$calc$的复杂度是$A$,$make(x,y)$的复杂度是$B$,操作有$M$个,$modify$和$get$中较大复杂度是$C$
那么复杂度是:
$$
\dfrac{AM}{P}+\dfrac{M}{P}BP^2+Cm \log_2m\\
\dfrac{AM}{P}+MBP+Cm\log_2m\\
当\dfrac{AM}{P}=MBP时最优\\
\dfrac{AM}{P}=MBP\\
MA=MBP^2\\
\dfrac{A}{B}=P^2\\
P=\sqrt{\dfrac A B}\\
总复杂度:O(M\sqrt{AB}+Cm\log_2m)\\
$$
卡点:无
C++ Code:
#include <cstdio> #include <cstring> #define maxn 100010 long long s[maxn]; int op[maxn], l[maxn], r[maxn]; long long val[maxn], ans[maxn]; long long tmp[maxn]; int n, m; #define Blo 317 inline int max(int a, int b) {return a > b ? a : b;} inline int min(int a, int b) {return a < b ? a : b;} inline int make(int x, int y) { return max(min(r[x], r[y]) - max(l[x], l[y]) + 1, 0); } void $(int l, int r) { if (r - l > Blo) { int mid = l + r >> 1; $(l, mid), $(mid + 1, r); memset(tmp, 0, sizeof tmp); for (int i = l; i <= mid; i++) if (op[i] == 1) { tmp[::l[i]] += val[i]; tmp[::r[i] + 1] -= val[i]; } for (int i = 1; i <= n; i++) tmp[i] += tmp[i - 1]; for (int i = 1; i <= n; i++) tmp[i] += tmp[i - 1]; for (int i = mid + 1; i <= r; i++) { if (op[i] == 2) ans[i] += tmp[::r[i]] - tmp[::l[i] - 1]; } } else { for (int i = l; i < r; i++) if (op[i] == 1) { for (int j = i + 1; j <= r; j++) if (op[j] == 2) { ans[j] += make(i, j) * val[i]; } } } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%lld", s + i); s[i] += s[i - 1]; } for (int i = 1; i <= m; i++) { scanf("%d%d%d", op + i, l + i, r + i); if (op[i] == 1) scanf("%lld", val + i); else ans[i] = s[r[i]] - s[l[i] - 1]; } $(1, m); for (int i = 1; i <= m; i++) if (op[i] == 2) printf("%lld\n", ans[i]); return 0; }