线段树延迟更新
概述
暑假集训的时候好多东西只学了个皮毛,,,对付模板题还能试试,,,但是一看一些稍难的一些题时,,,肯定单纯的套模板是不行得了,,,那样多没意思啊,,,
延迟更新之前就看到过,,,当初的映像就是在普通的线段树里加一个lazy,,,然后可以延迟更新区间,,,这在对区间整段的更新很有用,,,因为不用对更新区间的每一个点更新,,这样就能省下很多时间,,,
但是,,那时没时间也看不懂,,,跟别提怎么操作了,,,,
国庆的时候专门看看了看这块知识,,,大概了解了lazy的作用以及该怎么使用他,,
分析
单纯的线段树主要是 单点修改,区间查询 ,,,
若是不更改进行区间的修改时,,,只能对区间里的每一个数进行单点修改,,,当数据量很大时,,这样的操作很费时间,,,
所以可以对每一个节点都加一个lazy标记,,,当这一段要更新时,,父节点的lazy更新,,然后区间所维护的sum加上相应的几倍的lazy,,,,这样该节点对上时更新后的值,,向上正确,,,而对于它的两个子节点,,,只将lazy更新表明这里需要更新,,,但是并没有继续向下更新,,,这一段的操作由pushdown()函数完成,,,
实现和练习
看个具体的例子:题目链接,,,
题目意思很简单,,,就是初始长度为n的一个数列值全为1,,
然后对某些区间进行赋为1 , 2 , 3的操作,,,最后问你在这些操作之后这一段的和是多少,,,
具体的实现如下:
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define ll long long
const int maxn = 1e5 + 5;
//因为初始值全为一所以没有a[maxn]
struct tree
{
int l;
int r;
ll sum;
ll lazy; //lazy标记
}node[maxn << 2];
void pushup(int rt)
{
node[rt].sum = node[rt << 1].sum + node[rt << 1 | 1].sum;
}
void pushdown(int rt , int nl , int nr)
{
//rt指当前节点,,,
//nl指左节点有nl个需要被赋值为lazy
//同理,,nr指右节点有nr个需要被赋值为lazy
if(node[rt].lazy)
{
//当这节点lazy不为0时,,要向下更新一下
node[rt << 1].sum = node[rt].lazy * nl; //修改sum
node[rt << 1].lazy = node[rt].lazy; //下推lazy
node[rt << 1 | 1].sum = node[rt].lazy * nr;
node[rt << 1 | 1].lazy = node[rt].lazy;
node[rt].lazy = 0; //标记清除
}
}
void build(int rt , int l , int r)
{
node[rt].l = l;
node[rt].r = r;
node[rt].sum = 0;
node[rt].lazy = 0; //不要忘了
if(l == r)
{
node[rt].sum = 1;
return;
}
int mid = node[rt].l + ((node[rt].r - node[rt].l) >> 1);
build(rt << 1 , l , mid);
build(rt << 1 | 1 , mid + 1 , r);
pushup(rt);
return;
}
void update(int rt , int L , int R , int C)
{
if(L <= node[rt].l && node[rt].r <= R)
{
//当该节点对应的区间在所要操作的区间里时更新
node[rt].sum = (node[rt].r - node[rt].l + 1) * C;
node[rt].lazy = C;
return;
}
int mid = node[rt].l + ((node[rt].r - node[rt].l) >> 1);
//下推lazy标记,,想上保证正确
pushdown(rt , mid - node[rt].l + 1 , node[rt].r - mid);
if(L <= mid) update(rt << 1 , L , R , C);
if(R > mid) update(rt << 1 | 1 , L , R , C);
pushup(rt);
return;
}
ll query(int rt , int L , int R)
{
if(L <= node[rt].l && node[rt].r <= R)
{
return node[rt].sum;
}
int mid = node[rt].l + ((node[rt].r - node[rt].l) >> 1);
pushdown(rt , mid - node[rt].l + 1 , node[rt].r - mid);
ll ans = 0;
if(L <= mid) ans += query(rt << 1 , L , R);
if(R > mid) ans += query(rt << 1 | 1 , L , R);
return ans;
}
int main(int argc, char const *argv[])
{
int T;scanf("%d" , &T);
for(int i = 1; i <= T; ++i)
{
int n , q;
scanf("%d%d" , &n , &q);
build(1 , 1 , n);
while(q--)
{
int a , b , c;
scanf("%d%d%d" , &a , &b , &c);
update(1 , a , b , c);
}
printf("Case %d: The total value of the hook is %lld.\n" , i , query(1 , 1 , n));
}
return 0;
}
大概就是这么多,,,只要理解了lazy的作用,,,以及下推的意思,,,基本就ok了,,,,
(end)
剑之所指,心之所向,身之所往!!!