[洛谷P3372 【模板】线段树 1($分治)

题目大意:线段树$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;
}

  

猜你喜欢

转载自www.cnblogs.com/Memory-of-winter/p/9844235.html