【luogu P3372】【ybtoj】【线段树课堂过关】【例题2】区间查改 &【模板】线段树 1

【例题2】区间查改 & 【模板】线段树 1


Link

luogu P3372 【模板】线段树 1
ybtoj【线段树课堂过关】【例题2】区间查改
题面//因为不知道侵不侵权所以就是题面是私密的,有账号的直接看转送门就可了


题目大意

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

  1. 将某区间每一个数加上s。
  2. 求出某区间每一个数的和。

解题思路

模板带lazy标的线段树
对于每个需要修改的区间(例[1-2] + 5),并不寻找区间中单个单个的点([1-1] + 5,[2-2] + 5)
而是直接在区间上修改([1,2].s + 5 * 2),并且打上lazy标([1,2].lazy + 5)
等需要找区间中更小的区间时再把lazy标下传([1,1].s + 5,[1,1].lazy + 5;[2,2].s + 5,[2,2].lazy + 5)


Code

#include <iostream>
#include <cstdio>
#define ll long long

using namespace std;

struct DT{
	ll s, lazy;
}tree[8001000];
int n, m, c, x, y;
int s, a[1001000];

void down(int root, int l, int r) {  //下传lazy标
	int mid = (l + r) / 2;
	tree[root * 2].s += tree[root].lazy * (mid - l + 1);
	tree[root * 2].lazy += tree[root].lazy;
	
	tree[root * 2 + 1].s += tree[root].lazy * (r - (mid + 1) + 1);
	tree[root * 2 + 1].lazy += tree[root].lazy;
	
	tree[root].lazy = 0;
}

void build(int root, int l, int r) {
	if(l == r) {
		tree[root].s = a[l];
		return;
	}
	int mid = (l + r) / 2;
	build(root * 2, l, mid);
	build(root * 2 + 1, mid + 1, r);
	tree[root].s = tree[root * 2].s + tree[root * 2 + 1].s;
}

void add(int root, int l, int r, int x, int y, int s) {
	if (l >= x && r <= y) {
		tree[root].s += 1ll * s * (r - l + 1);
		tree[root].lazy += s;
		return;
	}
	down(root, l, r);  //需要寻找更小的区间=>下传
	int mid = (l + r) / 2;
	if(x <= mid)
		add(root * 2, l, mid, x, y, s);
	if(y > mid) 
		add(root * 2 + 1, mid + 1, r, x, y, s);
	tree[root].s = tree[root * 2].s + tree[root * 2 + 1].s;
}

ll find(int root, int l, int r, int x, int y) {
	if (l >= x && r <= y) return tree[root].s;
	down(root, l, r);
	int mid = (l + r) / 2;
	ll ans = 0;
	if(x <= mid)
		ans += find(root * 2, l, mid, x, y);
	if(y > mid)
		ans += find(root * 2 + 1, mid + 1, r, x, y);
	return ans;
}

int main() {
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	build(1, 1, n);
	for(int i = 1; i <= m; i++) {
		scanf("%d %d %d", &c, &x, &y);
		if(c == 1) {
			scanf("%d", &s);
			add(1, 1, n, x, y, s);
		} else
			printf("%lld\n", find(1, 1, n, x, y));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值