线段树延迟更新

Posted 31415926535x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树延迟更新相关的知识,希望对你有一定的参考价值。


title: 线段树延迟更新
date: 2018-10-10 18:50:49
tags:

  • acm
  • 算法
    categories:
  • ACM-线段树

概述

暑假集训的时候好多东西只学了个皮毛,,,对付模板题还能试试,,,但是一看一些稍难的一些题时,,,肯定单纯的套模板是不行得了,,,那样多没意思啊,,,

延迟更新之前就看到过,,,当初的映像就是在普通的线段树里加一个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.
" , i , query(1 , 1 , n));
    }
    return 0;
}

大概就是这么多,,,只要理解了lazy的作用,,,以及下推的意思,,,基本就ok了,,,,

(end)

以上是关于线段树延迟更新的主要内容,如果未能解决你的问题,请参考以下文章

C++ 树进阶系列之线段树和它的延迟更新

杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)

poj 2528 线段树+延迟更新

[uva11992]Fast Matrix Operations(多延迟标记,二维线段树,区间更新)

[HDOJ4578]Transformation(线段树,多延迟标记)

hdu 1698 Just a Hook 线段树成段更新