做这道题之前一直以为会用单点更新就行了,区间更新也可以转化为单点更新的问题,结果当然是超时
之后就是学习的过程,看了好长时间算是明白了区间更新的奥妙,即pushdown函数实现了标记下移更新
再更新一下,之前一直以为自己理解了,其实上面说的理解是一知半解,因为只有一个地方不懂而已,没有深究,后来发现其实自己一直都理解的有偏差,还是太急于求成了
之前一直不知道为什么在update里面用过pushdown了,而在query中还要再用一次,其实Lazy思想恰恰是暂时不更新rt子节点的sum[]值,到此就return,直到下次需要用到rt子节点的值的时候才去更新,这样避免许多可能无用的操作,从而节省时间,即在update中设置标记,在query中完成最终更新
Lazy标记还有一个奇妙之处就是他是可以叠加的,只要初始标记为0,各种标记都可以进行叠加
其中
PushUp(rt):通过当前节点rt把值递归向上更新到根节点
PushDown(rt):通过当前节点rt递归向下去更新rt子节点的值
rt表示当前子树的根,也就是当前所在的结点
为何先用pushdown再用pushup,就是因为先将懒惰标记移到字节点,在能保证下面的更新正确,之后再用pushup更新一下新的端点和。。。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define maxn 100005
long long sum[maxn << 2];//四倍数组存线段树
long long laz[maxn << 2];
void pushup(int rt)//常规更新操作
{
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushudown(int rt, int m)//区间更新核心操作,标记下移,rt为父节点,m为当前区间长度
{
if(laz[rt])//如果此处有标记(即非0)
{
sum[rt << 1] += (m-(m >> 1))*laz[rt];//更新子左端点值,即等于下一个区间元素和*lazy值
sum[rt << 1 | 1] += (m >> 1)*laz[rt];//更新子右端点值
laz[rt << 1] += laz[rt];//lazy标记下移
laz[rt << 1 | 1] += laz[rt];//同上
laz[rt] = 0;//清除父节点标记
}
}
void build(int l, int r, int rt)//常规建树
{
if(l == r)
{
scanf("%lld",&sum[rt]);
return ;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
pushup(rt);
}
void update(int left, int right, int c, int l, int r, int rt)
{
if(left <= l && right >= r)//更新lazy标记和线段树端点,遇到第一个符合条件的区间就return,而不是一直执行到末尾
{
laz[rt] += c;
sum[rt] += c*(r - l + 1);
return ;
}
pushudown(rt, r-l+1);//如果当前区间不是想找的区间,继续向下的时候需要用到pushup(),此时putdown是为了配合pushup使用
int m = (l + r) >> 1;
if(left <= m)update(left, right, c, l, m, rt << 1);
if(right > m)update(left, right, c, m + 1, r, rt << 1 | 1);
pushup(rt);
}
/*
单点更新模式
void update(int p, int add, int l, int r,int rt)//更新数据,需要更新的是包含p元素的节点,需要更新的值为add
{
if(l == r)//递归到线段树最后一层,此时每个区间只包含一个点
{
sum[rt] += add;
return ;
}
int m = (l + r) >> 1;
if(p <= m)update(p, add, l, m, rt << 1);//需要更新的地方
else update(p, add, m + 1, r, rt << 1 | 1);
pushup(rt);//更新
}
*/
long long query(int left, int right, int l, int r, int rt)//常规查询,多了一个pushdown()
{
if(left <= l && right >= r) return sum[rt];
pushudown(rt, r - l + 1);//此处继续上面的未完成的pushdown操作
int m = (l + r) >> 1;
long long ans = 0;
if(left <= m) ans += query(left, right, l, m, rt << 1);
if(right > m) ans += query(left, right, m + 1, r, rt << 1 | 1);
return ans;
}
int main()
{
int m, n;
scanf("%d%d",&n,&m);
memset(laz, 0, sizeof(laz));//初始无lazy标记
build(1, n, 1);
while(m --)
{
char ch;
int a, b, c;
getchar();//吸收回车
scanf("%c",&ch);
if(ch == 'Q')
{
scanf("%d%d",&a,&b);
printf("%lld\n",query(a,b,1,n,1));
}
else
{
scanf("%d%d%d",&a,&b,&c);
update(a, b, c, 1, n, 1);//更新区间(a,b)的值,更新值为c
}
}
return 0;
}