3.21

线段树

堆还没写好,就要被赶着去学线段树了。我们通过一道基础题进行线段树的引入

线段树模板

thinking

线段树用法,如题,如果我们采用前缀和进行查询区间的和,当发生修改的时候,我们需要对包括该部分区间的所有前缀值进行修改,不可避免地\(n^2\),而线段树通过对区间建树求和,复杂度降到了\(log n\)

我们使用完全二叉树进行建树操作,对于这个二叉树,我们只有两种操作,单点修改(add()),区间查询(calc()),由于二叉树地性质,我们均采用递归地形式进行书写

  • buildtree
/*
从k开始,对从l到r地区间进行建树
左子树表示[l,m]的和
右子树表示[m+1,r]的和
*/
inline void buildtree(int k, int l, int r) {
	if (l == r) {
		f[k] = a[l];
		return;
	}
	int m = (l + r) >> 1;
	buildtree(k + k, l, m);
	buildtree(k + k + 1, m + 1, r);
	f[k] = f[k + k] + f[k + k + 1];
}
  • add
/*
从k开始,对区间[l,r]的x下标,加上y
如果对于某一个数进行加上操作,则从跟节点到叶节点这一条路径都需要加和,我们只需判断每一次x再左子树还是右子树即可。
*/
inline void add(int k, int l, int r, int x, int y) {
	f[k] += y;
	if (l == r)
		return;
	int m = (l + r) >> 1;
	if (x <= m)
		add(k + k, l, m, x, y);
	else
		add(k + k + 1, m + 1, r, x, y);
}
  • calc
/*
从k开始,在区间[l,r],对[s,t]区间进行求和
查询区间和有如下几种情况:
	1、[l,s]与[s,t]完全相同,直接返回节点值即可
	2、区间不匹配
		2.1、在左半区间内,进行递归搜索即可
		2.2、在右半区间内,进行递归搜索即可
		2.3、横跨了中点分开的两个区间,将区间从中点分两半,分别进行递归求和。
*/
int calc(int k, int l, int r, int s, int t) {
	if (l == s && r == t)
		return f[k];
	int m = (l + r) >> 1;
	if (t <= m)
		return calc(k + k, l, m, s, t);
	else if (s > m)
		return calc(k + k + 1, m + 1, r, s, t);
	else
		return calc(k + k, l, m, s, m) + calc(k + k + 1, m + 1, r, m+1, t);
}

solution

const int N = 5*1e5;
int a[N + 5], f[4 * N + 4];

inline void buildtree(int k, int l, int r) {
	if (l == r) {
		f[k] = a[l];
		return;
	}
	int m = (l + r) >> 1;
	buildtree(k + k, l, m);
	buildtree(k + k + 1, m + 1, r);
	f[k] = f[k + k] + f[k + k + 1];
}

inline void add(int k, int l, int r, int x, int y) {
	f[k] += y;
	if (l == r)
		return;
	int m = (l + r) >> 1;
	if (x <= m)
		add(k + k, l, m, x, y);
	else
		add(k + k + 1, m + 1, r, x, y);
}

int calc(int k, int l, int r, int s, int t) {
	if (l == s && r == t)
		return f[k];
	int m = (l + r) >> 1;
	if (t <= m)
		return calc(k + k, l, m, s, t);
	else if (s > m)
		return calc(k + k + 1, m + 1, r, s, t);
	else
		return calc(k + k, l, m, s, m) + calc(k + k + 1, m + 1, r, m+1, t);
}

void solve() {
	int n, m; cin >> n >> m;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	buildtree(1, 1, n);
	for (int i = 0; i < m; ++i) {
		int index; cin >> index;
		if (index == 1) {
			int x, k; cin >> x >> k;
			add(1, 1, n, x, k);
		}
		else {
			int x, y; cin >> x >> y;
			cout << calc(1, 1, n, x, y) << '\n';
		}
	}
}
posted @ 2022-03-22 00:24  圣道  阅读(83)  评论(0)    收藏  举报