线段树lazy标记入门笔记

最近花近两个星期敲线(da)段(you)树(xi)。其实花了一节课磕磕巴巴就打出来了然而总是不过,然后开始漫长的找错征途。。。
此处省略一万行泪;
还好有题解和磊磊不然一年都找不出来错/捂脸


此处正文

codevs4919
线段树练习4

给你N个数,有两种操作

1:给区间[a,b]内的所有数都增加X

2:询问区间[a,b]能被7整除的个数

区间修改区间查询

结构体里添加sev [ 7 ]数组
sev [ k ] 表示当前区间模7余k的数的个数

然后是标记

一开始想到的是add时
如果add范围全覆盖到当前区间
当前区间修改
加到lazy
然后回去时沿路更新父节点值
query的时候遇到有lazy的区间就下传lazy

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define blank " " 
using namespace std;

ll read()
{
    ll ans=0;
    bool b=1;
    char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')b=0;
        c=getchar();
    }
    while(isdigit(c))
    {
        ans=ans*10+(c-'0');
        c=getchar();
    }
    if(b)return ans;
    else return -ans;
}
int a[1000010] , sevv[20];
struct tree{
    tree* lc,* rc;
    int l,r;
    int sev[7];
    int lazy;
    /*tree() {
        lazy=0;
        for(int i=0;i<=6;i++)
        {
            sev[i]=0;
        }
    }*/
}*root;
void pushup(tree* node)
{
    for(int i=0;i<=6;i++)
    {
        node->sev[i]=0;
    }
    for(int i=0;i<=6;i++)
    {
        node->sev[i]=node->lc->sev[i]+node->rc->sev[i];
    }
}
void pushdown(tree* node)
{
    if(node->lazy)
    {
        node->lc->lazy+=node->lazy;
        node->lc->lazy%=7;
        node->rc->lazy+=node->lazy;
        node->rc->lazy%=7;
        int s=node->lazy;
        for(int i=0;i<=6;i++)
        {
            sevv[i]=0;
        }
        for(int i=0;i<=6;i++)
        {
            sevv[(i+s)%7]=node->lc->sev[i];
            sevv[(i+s)%7+7]=node->rc->sev[i];
        }
        for(int i=0;i<=6;i++)
        {
            node->lc->sev[i]=sevv[i];
            node->rc->sev[i]=sevv[i+7];
        }
        node->lazy=0;
    }
} 
tree* build(int l,int r)
{
    tree* node=new tree;
    if(l==r)
    {
        for(int i=0;i<=6;i++)
        {
            node->sev[i]=0;
        }
        node->sev[a[l]]=1;
    }
    else 
    {
        int mid=(l+r)>>1;
        node->lc=build(l,mid);
        node->rc=build(mid+1,r);
        pushup(node);
    }
    node->l=l;node->r=r;
    node->lazy=0;
    return node;
}
void add(tree* node,int l,int r,int s)
{ 
    if(node->l==l&&node->r==r)
    {
        node->lazy+=s;
        for(int i=0;i<=6;i++)
        {
            sevv[i]=0;
        }
        for(int i=0;i<=6;i++)
        {
            sevv[(i+s)%7]=node->sev[i];
        }
        for(int i=0;i<=6;i++)
        {
            node->sev[i]=sevv[i];
        }
        return;
     }

        int mid=(node->l+node->r)>>1;
        if(r<=mid){

            add(node->lc,l,r,s);
        }
        else if(l>mid)
        {
            add(node->rc,l,r,s);
        }
        else {
            add(node->lc,l,mid,s);
            add(node->rc,mid+1,r,s);
        }
        pushup(node);
    return;
}
int query(tree* node,int l,int r)
{
    if(node->l==l&&node->r==r)
    {
        return node->sev[0];
    }
    else {
        pushdown(node);
        int mid=(node->l+node->r)>>1;
        if(r<=mid)
        {
            return query(node->lc,l,r);
        } 
        else if(l>mid)
        {
            return query(node->rc,l,r); 
        }
        else {
            return query(node->lc,l,mid)+query(node->rc,mid+1,r);
        }
    }
}
int main()
{
    //ios::sync_with_stdio(false);
    int n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read()%7;
    }
    root=build(1,n);
    int m=read();
    for(int i=1;i<=m;i++)
    {
        string s;
        cin>>s;
        if(s=="add")
        {
            int aa=read(),bb=read(),cc=read()%7; 
            add(root,aa,bb,cc);
        }
        else
        {
            int aa=read(),bb=read();
            int ans=query(root,aa,bb);
            cout<"\n";
        }
    }
    return 0;
}

以上是第一个版本
不出所料 听取wa声一片

然后听磊磊说
add的时候
如果碰到lazy标记的话
也要下传哟

我表示不服

于是乎
磊磊拿笔在纸上画了画

果然磊磊专治各种不服

我服了

第一个重点来了
为什么add的时候也要下传lazy

因为add写的是先修改目标区间然后回来时更新

如果不下传lazy的话

可以想一下

当前访问到了这个区间
但是父节点的区间有lazy
有lazy表示应该有修改但是儿子节点(就是当前这个区间)还没有修改
但是此时目的区间就是当前的区间
于是add修改了当前区间(还未加上父节点的lazy)的值,然后当前节点打上lazy 返回
注意他返回的时候是要顺带修改父节点的
要顺带修改父节点的!!
然后就直接把本来已经修改好的父节点
改成错的了。。

然后改了改

void add(tree* node,int l,int r,int s)
{ 
    pushdown(node);
    if(node->l==l&&node->r==r)
    {
        node->lazy+=s;
        for(int i=0;i<=6;i++)
        {
            sevv[i]=0;
        }
        for(int i=0;i<=6;i++)
        {
            sevv[(i+s)%7]=node->sev[i];
        }
        for(int i=0;i<=6;i++)
        {
            node->sev[i]=sevv[i];
        }
        return;
     }

        int mid=(node->l+node->r)>>1;
        if(r<=mid){

            add(node->lc,l,r,s);
        }
        else if(l>mid)
        {
            add(node->rc,l,r,s);
        }
        else {
            add(node->lc,l,mid,s);
            add(node->rc,mid+1,r,s);
        }
        pushup(node);
    return;
}

加个pushdown不就好了

然后

RE

后来找错实在找不出来对了下题解

题解大概是这么写的

void add(tree* node,int l,int r,int s)
{ 

    if(node->l==l&&node->r==r)
    {
        node->lazy+=s;
        for(int i=0;i<=6;i++)
        {
            sevv[i]=0;
        }
        for(int i=0;i<=6;i++)
        {
            sevv[(i+s)%7]=node->sev[i];
        }
        for(int i=0;i<=6;i++)
        {
            node->sev[i]=sevv[i];
        }
        return;
     }
     pushdown(node);
        int mid=(node->l+node->r)>>1;
        if(r<=mid){

            add(node->lc,l,r,s);
        }
        else if(l>mid)
        {
            add(node->rc,l,r,s);
        }
        else {
            add(node->lc,l,mid,s);
            add(node->rc,mid+1,r,s);
        }
        pushup(node);
    return;
}

pushdown的位置在下面

恍然大悟

我那个版本到最后一个节点再pushdown的话肯定会访问无效内存的啊!

嗨吖气死

那我改成这样不行么

void add(tree* node,int l,int r,int s)
{ 
    if(l!=r)pushdown(node);
    if(node->l==l&&node->r==r)
    {
        node->lazy+=s;
        for(int i=0;i<=6;i++)
        {
            sevv[i]=0;
        }
        for(int i=0;i<=6;i++)
        {
            sevv[(i+s)%7]=node->sev[i];
        }
        for(int i=0;i<=6;i++)
        {
            node->sev[i]=sevv[i];
        }
        return;
     }
        int mid=(node->l+node->r)>>1;
        if(r<=mid){

            add(node->lc,l,r,s);
        }
        else if(l>mid)
        {
            add(node->rc,l,r,s);
        }
        else {
            add(node->lc,l,mid,s);
            add(node->rc,mid+1,r,s);
        }
        pushup(node);
    return;
}

还真不行

依然是WA

为啥呢

因为又把node->l node->r 和l r 弄混了

前者是区间左右端点

后者是查询的区间端点

!!

以后可别弄混了 这是真难查

线段树好麻烦

THE END

你可能感兴趣的:(codevs)