区间查询 区间修改 线段树区间修改懒标记 T3 线段树 243. 一个简单的整数问题2 - AcWing题库
给定一个长度为 的数列 ,以及 条指令,每条指令可能是以下两种之一:
C l r d
,表示把 都加上 。Q l r
,表示询问数列中第 个数的和。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数 。
第二行 个整数 。
接下来 行表示 条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
,
,
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
4
55
9
15
思路
之前是用差分的小技巧拿树状数组做区间修改。这里直接用线段树来写。
在正常的单点查询模板中加上 lazy 技巧 和 pushdown 操作即可实现, 核心思想是当在 修改 区间时, 不去直接走到底把所有叶子结点改一遍, 而是只修改 代表整个区间的几个节点, 并在其上面标记 lazy 记录, 代表要把这个区间内修改。
标记完并更新节点的sum值后就直接返回, 当update或者query需要用到 区间里面的节点时, 在经过 节点便会执行 pushdown 操作, 把 之前记录的 lazy 标记扩散给子节点并更新子节点的sun值。 此时也不会直接更新到底部, 只是按需更新。
由此便可以优化很多时间复杂度。
pushdown函数中需要实现从当前节点的lazy修改子节点的sum和lazy:
Node &u = t[u], &l = t[u << 1], &r = t[u << | 1];
l.lazy += u.lazy, l.sum += u.lazy * (l.r - l.l + 1); // 左儿子区间共 l.r - l.l + 1 个数, 故需要都算上
r.lazy += u.lazy, r.sum += u.lazy * (r.r - r.l + 1);
u.lazy = 0; // 别忘了重置
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#define lson l,mid,u << 1
#define rson mid + 1, r, u << 1 | 1
using namespace std;
const int N = 1e5+ 10;
typedef long long ll;
struct Node {
int l,r;
ll sum, lazy;
}t[N << 2];
int n,m;
int a[N];
void pushup(int u)
{
t[u].sum = t[u << 1].sum + t[u << 1 | 1].sum;
}
void pushdown(int u)
{
Node &l = t[u << 1], &r = t[u << 1 | 1];
if(t[u].lazy)
{
l.sum += (ll)t[u].lazy * (l.r - l.l + 1);
l.lazy += t[u].lazy;
r.sum += (ll)t[u].lazy * (r.r - r.l + 1);
r.lazy += t[u].lazy;
t[u].lazy = 0;
}
}
void build(int l, int r, int u = 1)
{
t[u] = {l,r,0,0};
if(l == r)
{
t[u] = {l,r,a[l], 0};
return;
}
int mid = l + r >> 1;
build(lson), build(rson);
pushup(u);
}
void update(int L, int R, int val, int l = 1, int r = n, int u = 1)
{
if(L <= l && r <= R)
{
t[u].sum += (ll)val * (t[u].r - t[u].l + 1);
t[u].lazy += val;
return;
}
pushdown(u);
int mid = l + r >> 1;
if(L <= mid) update(L,R,val,lson);
if(R > mid) update(L,R,val,rson);
pushup(u);
}
ll query(int L, int R, int l = 1, int r= n, int u = 1)
{
if(L <= l && r <= R)
return t[u].sum;
pushdown(u);
int mid =l + r >> 1;
ll sum = 0;
if(L <= mid) sum += query(L, R, lson);
if(R > mid) sum += query(L,R, rson);
pushup(u);
return sum;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i];
build(1,n);
char op[2];
int l,r,x;
while(m--)
{
cin >> op >> l >> r;
if(op[0] == 'Q')
{
cout << query(l,r) << endl;
}
else {
cin >> x;
update(l,r,x);
}
}
return 0;
}