洛谷P3372 线段树1 模板题

呜呜呜一道模板题在懒节点上找错找了快一个小时我是飞舞。
洛谷传送门

题目

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

将某区间每一个数加上 kkk。
求出某区间每一个数的和。

输入格式

第一行包含两个整数 n,mn, mn,m,分别表示该数列数字的个数和操作的总个数。

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

接下来 mmm 行每行包含 333 或 444 个整数,表示一个操作,具体如下:

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

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

Input

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

Output

11
8
20

解题思路

主要是对懒节点的处理一定要娴熟。
代码实现了模块化。
用了结构体记录树。
看起来应该会好看一些。

代码

#include<bits/stdc++.h>
using namespace std;
#define lson left, mid, rt<<1
#define rson mid+1, right, rt<<1|1
#define ls rt<<1	//相当于rt*2
#define rs rt<<1|1 	//相当于rt*2+1
#define MAXN 1000010
#define IOS ios::sync_with_stdio(false);
#define FRE freopen("1.txt", "r", stdin)
#define ll long long int
int re[MAXN];   //记录要初始化的值
struct Tree {
	int left, right;
	ll val, lazy;
}tree[MAXN << 2]; //相当于MAXN*4
void push_up(int rt){
	if(tree[rt].left == tree[rt].right) return;
	tree[rt].val = tree[ls].val + tree[rs].val;
}
void buildTree(int left, int right, int rt) {
	if(left == right){
		tree[rt].left = tree[rt].right = left;
		tree[rt].val = re[left];
		tree[rt].lazy = 0;
		return;
	}
	tree[rt].left = left;
	tree[rt].right = right;
	tree[rt].lazy = 0;
	int mid = left + right >> 1;  //相当于(left + right) / 2,优先级比较低
	buildTree(lson);
	buildTree(rson);
	push_up(rt);
}
void down(int rt) {
	if(tree[rt].left == tree[rt].right){
		tree[rt].lazy = 0;
		return;
	}
	tree[rs].lazy += tree[rt].lazy;	//想一下为什么是 += 而不是 =
	tree[ls].lazy += tree[rt].lazy;
	tree[rs].val += (tree[rs].right - tree[rs].left + 1) * tree[rt].lazy;
	tree[ls].val += (tree[ls].right - tree[ls].left + 1) * tree[rt].lazy;
	tree[rt].lazy = 0;
}
void update(int left, int right, int rt, int add) {
	if(tree[rt].left == left && tree[rt].right == right){
		tree[rt].lazy += add;
		tree[rt].val += (tree[rt].right - tree[rt].left + 1) * add;
		return;
	}
	if(tree[rt].lazy) down(rt);
	int mid = tree[rt].left + tree[rt].right >> 1;
	if(left <= mid && right <= mid) update(left, right, ls, add);
	else if(left > mid) update(left, right, rs, add);
	else { update(lson, add); update(rson, add); }
	push_up(rt);
}
ll query(int left, int right, int rt){
	if(tree[rt].left == left && tree[rt].right == right){
		if(tree[rt].lazy) down(rt);
		return tree[rt].val;
	}
	if(tree[rt].lazy) down(rt);
	int mid = tree[rt].left + tree[rt].right >> 1;
	ll ans = 0;
	if(left <= mid && right <= mid) ans = query(left, right, ls);
	else if(left > mid) ans = query(left, right, rs);
	else { ans += query(lson); ans += query(rson); }
	return ans;
}
int main(int argc, char** argv) {
	IOS;	//加速
	int n, m; cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> re[i];
	}
	buildTree(1, n, 1);	//建树
	while(m--){
		int t; cin >> t;
		if(t == 1){
			int x, y, add; cin >> x >> y >> add;
			if(x > y) swap(x, y);
			update(x, y, 1, add);	//更新维护
		}else{
			int x, y; cin >> x >> y;
			if(x > y) swap(x, y);
			cout << query(x, y, 1) << endl;	//查区间值
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Raoxiaomi.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值