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
n≤8,
m
≤
10
m \le 10
m≤10。
对于 70% 的数据:
3
n
≤
10
3
3n \le {10}^3
3n≤103,
m
≤
10
4
m \le {10}^4
m≤104。
对于 100% 的数据:
1
≤
n
,
m
≤
10
5
1 \le n, m \le {10}^5
1≤n,m≤105。
保证任意时刻数列中任意元素的和在 [ − 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;
}