P3372 【模板】线段树 1(C++_线段树)

题目描述

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

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

输入格式

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

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

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

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

输出格式

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

输入输出样例

输入 #1

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

输出 #1

11
8
20

说明/提示

对于 30% 的数据: n ≤ 8 n \le 8 n8 m ≤ 10 m \le 10 m10
对于 70% 的数据: 3 n ≤ 10 3 3n \le {10}^3 3n103 m ≤ 10 4 m \le {10}^4 m104
对于 100% 的数据: 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1n,m105

保证任意时刻数列中任意元素的和在 [ − 2 63 , 2 63 ) [-2^{63}, 2^{63}) [263,263) 内。

Process

我觉得线段树的精华就在于他的懒标记,懒标记之所以懒,是因为他只做到你让他输出的这一步节点,而不会彻底的更新树(举个例子:给[3,6]加上1,那么因为懒标记的存在,他会给代表[3,6]的节点加上4个1,但是其实并不会给3、4、5、6分别加1,只有下次你要用到这几个节点的时候他才会一并加上)。
以上就是我对懒标记的理解,具体的线段树我觉得这篇博客就讲得很到位了:Senior Data Structure · 浅谈线段树(Segment Tree)

Code

#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define MAXN 1000010
ll n, m, a[MAXN<<2], tree[MAXN<<2], tag[MAXN<<2];
ll ls(ll x)
{
	return x << 1;
}
ll rs(ll x)
{
	return x << 1 | 1;
}
void push_up(ll p)//自下向上整合
{
	tree[p] = tree[ls(p)] + tree[rs(p)];
}
void build(ll p, ll l, ll r)//建树
{
	tag[p] = 0;
	if (l == r)
	{
		tree[p] = a[l];
		return;
	}
	ll mid = (r + l) >> 1;
	build(ls(p), l, mid);
	build(rs(p), mid + 1, r);
	push_up(p);//整合
}
void f(ll p, ll l, ll r, ll k)
{
	tag[p] += k;//子节点标记
	tree[p] += k * (r - l + 1);//标记下放(若是此节点之前的根节点没有过标记,那么k为零,不会改变数值;若是之前根节点有懒标记,那么向下把之前没做的补回来)
}
void pushdown(ll p, ll l, ll r)
{
	ll mid = (l + r) >> 1;
	f(ls(p), l, mid, tag[p]);
	f(rs(p), mid + 1, r, tag[p]);
	tag[p] = 0;//懒标记已经被下放,当前节点的标记去除
}
void update(ll x, ll y, ll curx, ll cury, ll k, ll p)
{
	if (x <= curx && y >= cury)
	{
		tree[p] += k * (cury - curx + 1);
		tag[p] += k;
		return;
	}
	pushdown(p, curx, cury);//懒标记下放
	ll mid = (curx + cury) >> 1;
	if (x <= mid)
		update(x, y, curx, mid, k, ls(p));
	if (y > mid)
		update(x, y, mid + 1, cury, k, rs(p));
	push_up(p);
}
ll query(ll x, ll y, ll curx, ll cury, ll p)
{
	ll ans = 0;
	if (x <= curx && y >= cury)
		return tree[p];
	ll mid = (curx + cury) >> 1;
	pushdown(p, curx, cury);
	if (x <= mid)
		ans += query(x, y, curx, mid, ls(p));
	if (y > mid)
		ans += query(x, y, mid + 1, cury, rs(p));
	return ans;
}
signed main()
{
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	build(1, 1, n);
	for (int i = 0; i < m; i++)
	{
		ll temp;
		ll x, y, k;
		scanf("%lld", &temp);
		switch (temp)
		{
			case 1:
			{
				scanf("%lld%lld%lld", &x, &y, &k);
				update(x, y, 1, n, k, 1);
				break;
			}
			case 2:
			{
				scanf("%lld%lld", &x, &y);
				printf("%lld\n", query(x, y, 1, n, 1));
				break;
			}
		}
	}
	return 0;
}
posted @ 2020-03-15 00:09  ccql  阅读(6)  评论(0编辑  收藏  举报  来源