线段树入门(ACM)

目录

百度解释

功能:

个人看法:

结构:

难点:

例题Tips:

例题1:加法+懒惰标记

例题2:加法+乘法+取模+懒惰标记

总结:


百度解释


功能:

延迟区间加乘,加速区间查询。

个人看法:

 

将区间不断二分,记录每段二分后区间,直到成点。

同时可以用结构体辅助记录每一段的信息:左右结点(区间范围),区间和,懒惰标记。

结构:

1.根据初始数据建立线段树:函数build(1,1,n):将数组a[n]的数据放在线段树上

代码中:   l:区间下标左极限。r:区间下标右极限。s:区间和。g:加法懒惰标记。gg:乘法懒惰标记。

2.加法(减法):函数add(1,x,y,k):在[x,y]区间(点)上加k  

3.乘法:函数mul(1,x,y,k): [x,y]区间(点)上乘k  

4.查询区间之和:函数search(1,x,y):返回[x,y]区间(点)之和

5.psuhdown(i)::先利用本结点的懒惰标记更新左右子结点的数据,并将本结点的懒惰标记放在左右子结点上,而本结点的懒惰标记恢复初始状态。

之所以懒惰,是因为控制了更新时机(只在将要遇到时)。

难点:

递归思想,二分,懒惰标记。

例题Tips:

1.位运算:

x>>1     x/2;   x>>n     x/pow(2,n);

x<<1     x*2;   x<<1     x*pow(2,n); 

2.懒惰标记:

同时有加乘懒惰标记时的标记使用顺序简单解释。

可以人为规定先后顺序。

先乘后加比先加后乘简单很多。

add: 
 if(l<=t[i].l&&t[i].r<=r)
    {
        t[i].g=(t[i].g+k)%p;
        t[i].s=(t[i].s+k*(t[i].r-t[i].l+1))%p;
        return ;
    }
    pushdown(i);
mul:  
if(l<=t[i].l&&t[i].r<=r)
    {
        t[i].s=(t[i].s*k)%p;
        t[i].g=(t[i].g*k)%p;
        t[i].gg=(t[i].gg*k)%p;
        return ;
    }
   pushdown(i);

void pushdown(LL i)
{
    t[i<<1].s=(t[i<<1].s*t[i].gg+t[i].g*(t[i<<1].r-t[i<<1].l+1))%p;
    t[(i<<1)+1].s=(t[(i<<1)+1].s*t[i].gg+t[i].g*(t[(i<<1)+1].r-t[(i<<1)+1].l+1))%p;
    //
    t[i<<1].gg=(t[i<<1].gg*t[i].gg)%p;
    t[(i<<1)+1].gg=(t[(i<<1)+1].gg*t[i].gg)%p;
    //
    t[i<<1].g=(t[i<<1].g*t[i].gg+t[i].g)%p;
    t[(i<<1)+1].g=(t[(i<<1)+1].g*t[i].gg+t[i].g)%p;
    t[i].g=0;
    t[i].gg=1;
}

例题1:加法+懒惰标记

题目背景 

P3372 【模板】线段树 1

同时适用于

P3374 【模板】树状数组 1 

P3368 【模板】树状数组 2

#include<bits/stdc++.h>
#define bbn 100005
// 注意bbn大小(会RE||ME)
using namespace std;
typedef long long int LL;
LL a[bbn];
struct T
{
    LL l,r;
    LL s,g;

} t[4*bbn];
void build(LL i,LL l,LL r)
{
    t[i].l=l;
    t[i].r=r;
    if(l==r)
    {
        t[i].s=a[l];
        return ;
    }
    LL mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)+1,mid+1,r);
    t[i].s=t[i<<1].s+t[(i<<1)+1].s;
}
void pushdown(LL i)
{
    if(t[i].g!=0)
    {
        t[i<<1].g+=t[i].g;
        t[i<<1].s+=t[i].g*(t[i<<1].r-t[i<<1].l+1);

        t[(i<<1)+1].g+=t[i].g;
        t[(i<<1)+1].s+=t[i].g*(t[(i<<1)+1].r-t[(i<<1)+1].l+1);

        t[i].g=0;
    }
}
LL search(LL i,LL l,LL r)
{
    if(l<=t[i].l&&t[i].r<=r)
    {
        return t[i].s;
    }
    if(t[i].r<l||t[i].l>r)
    {
        return 0;
    }
    pushdown(i);
    LL sum=0;
    LL mid=(t[i].l+t[i].r)>>1;
    if(l<=mid)
    {
        sum+=search(i<<1,l,r);
    }
    if(r>=mid+1)
    {
        sum+=search((i<<1)+1,l,r);
    }
    return sum;
}
void add(LL i,LL l,LL r,LL k)
{
    if(l<=t[i].l&&t[i].r<=r)
    {
        t[i].s+=k*(t[i].r-t[i].l+1);
        t[i].g+=k;
        return ;
    }
    pushdown(i);
    LL mid=(t[i].l+t[i].r)>>1;
    if(l<=mid)
    {
        add(i<<1,l,r,k);
    }
    if(r>=mid+1)
    {
        add((i<<1)+1,l,r,k);
    }

    t[i].s=t[i<<1].s+t[(i<<1)+1].s;
}
int main()
{
    LL n,m;
    scanf("%lld%lld",&n,&m);
    for(LL i=1; i<=n; i++)
    {
        scanf("%lld",&a[i]);
    }
    build(1,1,n);
    for(LL i=1; i<=m; i++)
    {
        LL judge;
        scanf("%lld",&judge);
        if(judge==1)
        {
            LL x,y,k;
            scanf("%lld%lld%lld",&x,&y,&k);
            add(1,x,y,k);
        }
        else if(judge==2)
        {
            LL x,y;
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",search(1,x,y));
        }
    }
}

例题2:加法+乘法+取模+懒惰标记

题目背景 

P3373 【模板】线段树 2

#include<bits/stdc++.h>
#define bbn 500001
using namespace std;
typedef long long int LL;
LL a[bbn];
LL n,m,p;
struct T
{
    LL l,r;
    LL s,g,gg;

} t[4*bbn];
void build(LL i,LL l,LL r)
{
    t[i].l=l;
    t[i].r=r;
    t[i].g=0;
    t[i].gg=1;
    if(l==r)
    {
        t[i].s=a[l]%p;
        return ;
    }
    LL mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)+1,mid+1,r);
    t[i].s=(t[i<<1].s+t[(i<<1)+1].s)%p;
}
void pushdown(LL i)
{
    t[i<<1].s=(t[i<<1].s*t[i].gg+t[i].g*(t[i<<1].r-t[i<<1].l+1))%p;
    t[(i<<1)+1].s=(t[(i<<1)+1].s*t[i].gg+t[i].g*(t[(i<<1)+1].r-t[(i<<1)+1].l+1))%p;
    //
    t[i<<1].gg=(t[i<<1].gg*t[i].gg)%p;
    t[(i<<1)+1].gg=(t[(i<<1)+1].gg*t[i].gg)%p;
    //
    t[i<<1].g=(t[i<<1].g*t[i].gg+t[i].g)%p;
    t[(i<<1)+1].g=(t[(i<<1)+1].g*t[i].gg+t[i].g)%p;
    t[i].g=0;
    t[i].gg=1;
}
LL search(LL i,LL l,LL r)
{
    if(t[i].r<l||t[i].l>r)
    {
        return 0;
    }
    if(l<=t[i].l&&t[i].r<=r)
    {
        return t[i].s;
    }
    pushdown(i);
    return (search(i<<1,l,r)+search((i<<1)+1,l,r))%p;
}
void mul(LL i,LL l,LL r,LL k)
{
    if(l<=t[i].l&&t[i].r<=r)
    {
        t[i].s=(t[i].s*k)%p;
        t[i].g=(t[i].g*k)%p;
        t[i].gg=(t[i].gg*k)%p;
        return ;
    }
    pushdown(i);
    LL mid=(t[i].l+t[i].r)>>1;
    if(l<=mid)
    {
        mul(i<<1,l,r,k);
    }
    if(r>=mid+1)
    {
        mul((i<<1)+1,l,r,k);
    }
    t[i].s=(t[i<<1].s+t[(i<<1)+1].s)%p;
}

void add(LL i,LL l,LL r,LL k)
{
    if(l<=t[i].l&&t[i].r<=r)
    {
        t[i].g=(t[i].g+k)%p;
        t[i].s=(t[i].s+k*(t[i].r-t[i].l+1))%p;
        return ;
    }
    pushdown(i);
    LL mid=(t[i].l+t[i].r)>>1;
    if(l<=mid)
    {
        add(i<<1,l,r,k);
    }
    if(r>=mid+1)
    {
        add((i<<1)+1,l,r,k);
    }

    t[i].s=(t[i<<1].s+t[(i<<1)+1].s)%p;
}
int main()
{

    scanf("%lld%lld%lld",&n,&m,&p);
    for(LL i=1; i<=n; i++)
    {
        scanf("%lld",&a[i]);
    }
    build(1,1,n);
    for(LL i=1; i<=m; i++)
    {
        LL judge;
        scanf("%lld",&judge);
        if(judge==1)
        {
            LL x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            mul(1,x,y,z);
        }
        else if(judge==2)
        {
            LL x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            add(1,x,y,z);

        }
        else if(judge==3)
        {
            LL x,y;
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",search(1,x,y));
        }
    }
}

总结:

时间仓促,细节不到。

记录学习,欢迎指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值