码迷,mamicode.com
首页 > 其他好文 > 详细

线段树懒惰点标记更新 hduHDU - 1698 Just a Hook

时间:2019-01-23 20:29:23      阅读:294      评论:0      收藏:0      [点我收藏+]

标签:info   区间更新   acm   hdu   [1]   定义   标记   oid   pushd   

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1698

题意:自行读题

解题思想:线段树原更新一次只能更新一个叶子节点,并更新此叶子结点以上所有相关的点,当一个区间做相同更新时,叶子节点以上的相关节点不断更新,时间复杂度增加。为节省时间,为每个点添加懒惰标记。自定义节点范围(l,r为求子节点区间和),懒惰点标记(lazy储存变化值),节点和(value节点区间和)。具体实现过程:当更新一个区间时,标记该区间的父亲节点为懒惰区间(祖宗节点的值不用标记,只要更新),同时更新涵盖该区间的节点,而该区间暂时懒得去更新,等下一次更新的区间与原被标记的区间有重合时,则此时将原标记区间往下标记并更新,同时父亲节点因为更新了,没懒惰了,则取消懒惰标记,即懒惰标记传给了子节点,直到重合区间可以以子节点为单位时就可以偷懒不用往下更新了,然后以相同懒惰更新的方式更新这次要更新的区间。

样例代码图解:

下图为初始化树,圈内为value值,下标为范围。

技术分享图片

 第一次更新后1-5更新为2时后的树,红色为标记点,发现1-5的子节点并没有更新,因为计算总和只和1-5这个点有关,更新这个点就行了。

技术分享图片

 第二次更新:在访问点5-9时发现5所在的1-5点被标记了,标记点下推,并更新子节点的值。

技术分享图片

 第二次5-9区间更新后的树以及标记的点。

技术分享图片

 

 根据图一下子就可以看出懒惰标记的意思来了,结合代码就方便多了,我画了很久的图QAQ。

具体代码如下:

#include<bits/stdc++.h>

const int maxn=100000;
using namespace std;

struct nodetree{
    int l,r;
    int value,lazy;
}tree[maxn<<2];

void pushup(int now)
{
    tree[now].value=tree[now<<1].value+tree[now<<1|1].value;
}

void build(int l,int r,int n)
{
    tree[n].l=l;
    tree[n].r=r;
    tree[n].lazy=0;
    if(l==r)
    {
        tree[n].value=1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,n<<1);
    build(mid+1,r,n<<1|1);
    pushup(n);
}
void pushdown(int n)
{
    if(tree[n].lazy!=0)
    {
        tree[n<<1].lazy=tree[n<<1|1].lazy=tree[n].lazy;
        tree[n<<1].value=(tree[n<<1].r-tree[n<<1].l+1)*tree[n<<1].lazy;
        tree[n<<1|1].value=(tree[n<<1|1].r-tree[n<<1|1].l+1)*tree[n<<1|1].lazy;
        tree[n].lazy=0;
    }
}
void update(int l,int r,int le,int ri,int n,int va)
{
    if(r<le||l>ri)
        return;
    if(l>=le&&r<=ri)
    {
        tree[n].lazy=va;
        tree[n].value=tree[n].lazy*(tree[n].r-tree[n].l+1);
        return;
    }
    pushdown(n);
    int mid=(l+r)>>1;
    if(mid>=le) update(l,mid,le,ri,n<<1,va);
    if(mid<ri) update(mid+1,r,le,ri,n<<1|1,va);
    pushup(n);
}
int main()
{
    int T;
    while(~scanf("%d",&T))
    {
        int count=1;
        while(T--)
        {
            int n,t,l,r,va;
            scanf("%d",&n);
            build(1,n,1);
            scanf("%d",&t);
            while(t--)
            {
                scanf("%d %d %d",&l,&r,&va);
                update(1,n,l,r,1,va);
            }
            printf("Case %d: The total value of the hook is %d.\n",count++,tree[1].value);
        }
    }
    return 0;
}

  最后说一下点的value是通过数学计算算出来的。

线段树懒惰点标记更新 hduHDU - 1698 Just a Hook

标签:info   区间更新   acm   hdu   [1]   定义   标记   oid   pushd   

原文地址:https://www.cnblogs.com/wwq-19990526/p/10311109.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!