线段树

洛谷模板题

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 k k k
  2. 求出某区间每一个数的和。
输入格式
  1. 第一行包含两个整数 n , m n, m n,m 分别表示该数列数字的个数和操作的总个数

  2. 第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i项的初始值。

  3. 接下来 m m m 行每行包含 3 或 4 个整数,表示一个操作,具体如下:

1   x   y   k 1\, x\, y\, k 1xyk:将区间 [ x , y ] [x, y] [x,y] 内每个数加上 k k k
2   x   y   2 \,x \,y\, 2xy:输出区间 [ x , y ] [x, y] [x,y] 内每个数的和。

输出格式

输出包含若干行整数,即为所有操作 2 的结果。
70% AC代码 终于能凭一己之力写出线段树代码了

#include<iostream>
#include<cstdio>
using namespace std;
const int maxN = 100000;
int arr[maxN];
long long tree[4*maxN];
//建立线段树 
void build(int node, int l, int r) {
	
	//递归的出口
	if (l == r) tree[node] = arr[l];
	else {
		int mid = (l + r) / 2;
		int lchild = (node << 1) + 1;
		int rchild = (node << 1) + 2;

		build(lchild, l, mid);
		build(rchild, mid + 1, r);
		//计算父节点的值
		tree[node] = tree[lchild] + tree[rchild];
	}
}
//将区间【ql,qr】内的数加上k
void update(int node, int l, int r, int ql, int qr, int k) {
	int mid = (l + r) / 2;
	int lchild = (node << 1) + 1;
	int rchild = (node << 1) + 2;
	//递归出口
	if (qr<l || ql>r) return;
	if (l == r) tree[node] += k;
	//当前区间与目标区间有重叠
	else {
		update(lchild, l, mid, ql, qr, k);
		update(rchild, mid + 1, r, ql, qr, k);
		tree[node] = tree[lchild] + tree[rchild];
	}
}

//查询【ql,qr】区间之和
long long query(int node, int l, int r, int ql, int qr) {
	int mid = (l + r) / 2;
	int lchild = (node << 1) + 1;
	int rchild = (node << 1) + 2;
	if (l > qr || ql > r) return 0;

	//如果目标区间包含当前区间返回 当前节点的值
	else if (ql<=l && r <= qr) return tree[node];
	else {
		long long  lsum = query(lchild, l, mid, ql, qr);
		long long  rsum = query(rchild, mid + 1, r, ql, qr);
		return lsum + rsum;
	}
}
int main() {
	int n = 0,m=0;
	scanf("%d%d", &n, &m);
	//原数组赋初值
	for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
	build(0, 0, n-1);

	for (int i = 0; i < m; i++) {
		int opr = 0, l = 0, r = 0, k = 0;
		scanf("%d", &opr);
		if (opr == 1) {
			scanf("%d%d%d", &l, &r, &k);
			update(0, 0, n - 1, l-1, r-1, k);
		}
		else {
			scanf("%d%d", &l, &r);
			cout << query(0, 0, n - 1, l-1, r-1)<<endl;
		}
	}
	
	return 0;
}

上面的代码只能过70%的数据,即使开再大的数组也只能过70%
下面的代码优化了,加入了lazytag 通过了全部样例🤣

#include<iostream>
#include<cstdio>
using namespace std;
const int maxN = 100000;
int arr[maxN];
long long tree[4*maxN],mark[4*maxN];
void build(int node, int l, int r) {
	int mid = (l + r) / 2;
	int lchild = (node << 1) + 1;
	int rchild = (node << 1) + 2;
	if (l == r) tree[node] = arr[l];
	else {
		build(lchild, l, mid);
		build(rchild, mid + 1, r);
		tree[node] = tree[lchild] + tree[rchild];
	}
}
void push_down(int node, int l, int r) {
	int mid = (l + r) / 2;
	int lchild = (node << 1) + 1;
	int rchild = (node << 1) + 2;

	mark[lchild] += mark[node];
	mark[rchild] += mark[node];

	tree[lchild] += (mid - l + 1)*mark[node];
	tree[rchild] += (r - mid)*mark[node];
    mark[node]=0;

}
//将【ql,qr】区间内的每个数加上k
void update(int node, int l, int r, int ql, int qr, int k) {
	int mid = (l + r) / 2;
	int lchild = (node << 1) + 1;
	int rchild = (node << 1) + 2;
	if (qr<l || ql>r) return;
	else if (ql <= l && r <= qr) {
		tree[node] += (r-l+1)*k;
		if (l < r)
			mark[node] += k;
	}
	else {
		//如果有lazytag就传递下去
        if(mark[node]) push_down(node, l, r);

		update(lchild, l, mid, ql, qr, k);
		update(rchild, mid + 1, r, ql, qr, k);
		tree[node] = tree[lchild] + tree[rchild];
	}
}

long long query(int node, int l, int r, int ql, int qr) {
	int mid = (l + r) / 2;
	int lchild = (node << 1) + 1;
	int rchild = (node << 1) + 2;
	if (ql > r || qr < l) return 0;
	else if (ql <= l && r <= qr) {
		return tree[node];
	}
	else {
        if(mark[node]) push_down(node, l, r);
		return query(lchild, l, mid, ql, qr) + query(rchild, mid + 1, r, ql, qr);

	}
}
int main() {
	int n=0, m=0;
	scanf("%d%d",&n,&m);
	for (int i = 0; i < n; i++) scanf("%d",&arr[i]);
	build(0, 0, n - 1);
	for (int i = 0; i < m; i++) {
		int opr = 0, x = 0, y = 0, k = 0;
		scanf("%d",&opr);
		if (opr == 1) {
			scanf("%d%d%d",&x,&y,&k);
			update(0, 0, n - 1, x-1, y-1, k);
		}
		else {
			scanf("%d%d",&x,&y);
			cout << query(0, 0, n - 1, x - 1, y - 1)<<endl;
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值