线段树延迟更新



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.\n" , i , query(1 , 1 , n));
    }
    return 0;
}

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

(end)

原文地址:https://www.cnblogs.com/31415926535x/p/9768560.html

时间: 2024-10-11 07:50:59

线段树延迟更新的相关文章

hdu 5023 线段树延迟更新+状态压缩

/* 线段树延迟更新+状态压缩 */ #include<stdio.h> #define N 1100000 struct node { int x,y,yanchi,sum; }a[N*4]; int lower[31]; void build(int t,int x,int y) { a[t].x=x; a[t].y=y; a[t].yanchi=0; if(x==y){ a[t].sum=lower[1]; return ; } int temp=t<<1; int mid=

poj 2528 线段树+延迟更新

题目链接:http://poj.org/problem?id=2528 题意: 在墙上贴海报,输入n(1<=n<=10000),表示n张海报,后n行输入 两整数l,r  ( 1<= l, r<= 1e9 ),表示海报从编号为l的石头一直贴到编号为r的石头,输入顺序即为粘贴顺序.问n张贴完之后,还能看到多少张海报. 思路: 显然区间操作,很容易联想到线段树操作,只不过区间 l,r 最大范围可达1e9,直接建树,内存必爆.   那么就需要避开1e9的数据,进行离散化,将区间变成(1到n

线段树 延迟更新

fafu 1008 1 #include<stdio.h> 2 #include<string.h> 3 const int N=100000+5; 4 int n, q; 5 int a[N], tree[N<<2], t[N<<2]; 6 7 void build(int rt, int l, int r) 8 { 9 if(l==r) 10 tree[rt]=10; 11 else 12 { 13 int m=(l+r)>>1; 14 bu

线段树区间更新

区间更新也可以分割成若干个子区间, 每层的结点至多选取 2 个,时间复杂度 O(logn). 懒惰(Lazy)标记 懒惰标记,也可称为延迟标记.一个区间可以转化为若干个结点,每个结点设一个标记,记录这个结点被进行了某种修改操作(这种修改操作会影响其子结点). 也就是说,仅修改到这些结点,暂不修改其子结点:而后决定访问其子节点时,再下传懒惰 (Lazy) 标记,并消除原来的标记. 优点在于,不用将区间的所有值暴力更新,大大提高效率. 在区间修改的一类问题中,我们可以设一个 delta 域,表示该节

POJ 3468 A Simple Problem with Integers(线段树区间更新)

题目地址:POJ 3468 打了个篮球回来果然神经有点冲动..无脑的狂交了8次WA..居然是更新的时候把r-l写成了l-r... 这题就是区间更新裸题.区间更新就是加一个lazy标记,延迟标记,只有向下查询的时候才将lazy标记向下更新.其他的均按线段树的来就行. 代码如下: #include <iostream> #include <cstdio> #include <cstring> #include <math.h> #include <stac

hdu 5023 A Corrupt Mayor&#39;s Performance Art(线段树区间更新)

#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; int tree[5001000],add[5001000]; int color[50]; int n,m; void pushup(int pos) { tree[pos]=tree[pos<<1]|tree[pos<<1|1]; //更新

poj-----(2528)Mayor&#39;s posters(线段树区间更新及区间统计+离散化)

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 43507   Accepted: 12693 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

POJ 2777 &amp;&amp; ZOJ 1610 &amp;&amp;HDU 1698 --线段树--区间更新

直接将这3题 放一起了  今天在做线段树的东西 这3个都是区间更新的 查询方式互相不同 反正都可以放到一起吧 直接先上链接了 touch me touch me touch me 关于涉及到区间的修改 -- 区间更新的话 分为 增减 或者 修改 主要就是个 laze 标记 就是延迟更新 对于区间更新的写法 一般是有2种 其一 仔细划分到每个细小的区间    另一 粗略划分 反正 ==我的代码里会给出2种写法 看自己喜好 hdu 1 //线段树 成段更新 ---> 替换 根结点的查询 2 3 #i

HDU 1698 Just a Hook (线段树延迟标记(lazy))

题意:有n个数初始值都为1,m个操作a,b,c,表示把区间[a,b]变为c,求最后n个数的和. 经典区间更新求和问题,需要用到延迟标记(或者说是懒惰标记),简单老说就是每次更新 的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新或询问的时候. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #include<cm