线段树优化——lazytag

9 篇文章 1 订阅

线段树嘛,NOIP的重点,手打模板理解一发(部分借鉴notonlysuccess的线段树,侵删)
题目:线段树模板——洛谷
前言
朴素线段树都是有以下几个操作:
1.query(区间求和)
代码如下

    int Query(int L,int R,int l,int r,int root)
    {
        if(L<=l&&R>=r) return tree[root];
        int mid=(l+r)>>1;
        int ret=0;
        if(L<=mid) ret+=Query(L,R,lson);
        if(R>mid) ret+=Query(L,R,rson);
        return ret;
    }

2.update(单点增减)

    void UpDate(int p,int add,int l,int r,int root)
    {
        if(l==r)
        {
            tree[root]+=add;
            return;
        }
        int mid=(l+r)>>1;
        if(p<=mid)  UpDate(p,add,lson);
        if(p>mid) UpDate(p,add,rson);
        Push_Up(root);
    }

当然树的初始化是必要的
1.建树

    void Build_Tree(int l,int r,int root)
    {
        if(l==r)
        {
            tree[root]=a[l];
            return;
        } 
        int mid=(l+r)>>1;
        Build_Tree(lson);
        Build_Tree(rson);
        Push_Up(root);
    }

2.权值的更新

    void Push_Up(int root)
    {
        tree[root]=tree[root<<1]+tree[root<<1|1];
    }

所以朴素线段树的代码如下

#include<cstdio>
#include<iostream>
#include<cstring>

#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1

using namespace std;

int a[5100];
int tree[22000];
char que[10];
int t,n;

void Push_Up(int root)
{
    tree[root]=tree[root<<1]+tree[root<<1|1];
}

void Build_Tree(int l,int r,int root)
{
    if(l==r)
    {
        tree[root]=a[l];
        return;
    } 
    int mid=(l+r)>>1;
    Build_Tree(lson);
    Build_Tree(rson);
    Push_Up(root);
}

int Query(int L,int R,int l,int r,int root)
{
    if(L<=l&&R>=r) return tree[root];
    int mid=(l+r)>>1;
    int ret=0;
    if(L<=mid) ret+=Query(L,R,lson);
    if(R>mid) ret+=Query(L,R,rson);
    return ret;
}

void UpDate(int p,int add,int l,int r,int root)
{
    if(l==r)
    {
        tree[root]+=add;
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid)  UpDate(p,add,lson);
    if(p>mid) UpDate(p,add,rson);
    Push_Up(root);
}

int main()
{
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        printf("Case %d:\n",i);
        scanf("%d",&n);
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a[j]);
        }
        Build_Tree(1,n,1);
        while(scanf("%s",&que))
        {
            if(que[0]=='E') break;
            else
            {
                int a,b;
                scanf("%d%d",&a,&b);    
                if(que[0]=='Q')
                {
                    printf("%d\n",Query(a,b,1,n,1));
                }
                if(que[0]=='A') UpDate(a,b,1,n,1);
                if(que[0]=='S') UpDate(a,-b,1,n,1);
            }
        }
        return 0;
    }
} 

正文
———————————————————————————
大多数线段树不一定是单点加/减权值,会在区间里集体增减。这样会使时间复杂度变得十分……
所以用了【懒惰标记】来优化
(下一段来自notonlysuccess)
成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候
具体操作代码如下(我也不是很懂,今晚听dalao讲QAQ)

void pushdown(int root,int mid)
{
    if(col[root])
    {
        col[root<<1]+=col[root];
        col[root<<1|1]+=col[root];
        tree[root<<1]+=col[root]*(mid-(mid>>1));
        tree[root<<1|1]+=col[root]*(mid>>1);
        col[root]=0;
    }
}

完整代码如下

#include<iostream> 
#include<cstdio>

#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
using namespace std;


long long a[110000],tree[410000],col[410000];
int n,m;

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

void pushdown(int root,int mid)
{
    if(col[root])
    {
        col[root<<1]+=col[root];
        col[root<<1|1]+=col[root];
        tree[root<<1]+=col[root]*(mid-(mid>>1));
        tree[root<<1|1]+=col[root]*(mid>>1);
        col[root]=0;
    }
}

void buildtree(int l,int r,int root)
{
    col[root]=0;
    if(l==r)
    {
        tree[root]=a[l];
        return;
    } 
    int mid=(l+r)>>1;
    buildtree(lson);
    buildtree(rson);
    pushup(root);
}

void update(int L,int R,int add,int l,int r,int root)
{
    if(L<=l&&r<=R)
    {
        col[root]+=add;
        tree[root]+=(long long)add*(r-l+1);
        return;
    }
    pushdown(root,r-l+1);
    int mid=(l+r)>>1;
    if(L<=mid)  update(L,R,add,lson);
    if(R>mid) update(L,R,add,rson);
    pushup(root); 
}

long long query(int L,int R,int l,int r,int root)
{
    if(L<=l&&R>=r) return tree[root];
    pushdown(root,r-l+1);
    int mid=(l+r)>>1;
    long long ret=0;
    if(L<=mid) ret+=query(L,R,lson);
    if(R>mid) ret+=query(L,R,rson);
    return ret;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    buildtree(1,n,1);
    char p;
    for(int i=1;i<=m;i++)
    {
        cin>>p;
        int b,c,d;
        if(p=='1')
        {
            cin>>b>>c>>d;
            update(b,c,d,1,n,1);
        }
        if(p=='2')
        {
            cin>>b>>c;
            printf("%lld\n",query(b,c,1,n,1));
        }
    }
    return 0;
}

最后补一发ZYHdalao的线段树模板orz

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
const int N=400000;
long long f[N],opt,n,m,k,opy,opx,opr,opl,ans,a[N],tag[N],size[N];
inline long long F()
{
    register long long aa,bb;register char ch;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
    while(ch=getchar(),ch>='0'&&ch<='9')aa=(aa<<3)+(aa<<1)+ch-'0';return bb?aa:-aa;
}
void build(long long x,long long l,long long r)
{
    if (l==r)
    {
        f[x]=a[l];
        size[x]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    f[x]=f[x<<1]+f[x<<1|1];
    size[x]=size[x<<1]+size[x<<1|1];
}

void down(int x)
{
    if (tag[x]!=0)
    {
        tag[x<<1]+=tag[x];
        tag[x<<1|1]+=tag[x];
        f[x<<1]+=size[x<<1]*tag[x];
        f[x<<1|1]+=size[x<<1|1]*tag[x];
        tag[x]=0;
    }
}
void calc(long long x,long long l,long long r)//1??,0?? 
{
    if (opl<=l&&opr>=r)
    {
        if (opt)ans+=f[x];
        else tag[x]+=opy,f[x]+=size[x]*opy;
        return ;
    }
    int mid=(l+r)>>1;
    down(x);
    if (opl<=mid)calc(x<<1,l,mid);
    if (opr>mid)calc(x<<1|1,mid+1,r);
    f[x]=f[x<<1]+f[x<<1|1];
}
int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)a[i]=F();
    build(1,1,n);
    int op;
//  for (int i=1;i<=n;i++)
//  cout<<f[i]<<' ';
    while(m--)
    {
        op=F();
        if (op==1)
        {
            opt=0;
            opl=F(),opr=F(),opy=F();
            calc(1,1,n);
        }
        else if (op==2)
        {
            opt=1;
            ans=0;
            opl=F(),opr=F();
            calc(1,1,n);
            cout<<ans<<endl;
        }
    }
    return 0;
}

To be continue…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值