个人学习总结——线段树


前言

周围人都写很长一段时间的博客了,感觉自己不写,有点过不去,从今天开始试着写博客吧,so,这就是我的第一篇博客啦(/▽\)。。。


第一篇博客主要是想写一写线段树的学习和应用,以及个人的理解

一、线段树意义及作用

线段树就是一种数据结构,可以方便我们更好的查询和修改数据,日常做题的话他就是更支持数组数据的查询和修改,在竞赛方面提供更多的思考方向

二、主要操作

1.建树

建树过程我们主要采取递归建树,通过当前节点的区间向下建树,每个节点都将区间对半分下传到子节点,叶子节点是节点自身
代码如下(示例):

ll build(int z,int l,int r)
{
        d[z].l=l,d[z].r=r;
        if(l==r) 
        {
            d[z].sum=a[l];
            return d[z].sum;
        }
        int mid=l+r>>1;
        d[z].sum+=(ll)build(z*2,l,mid);

        d[z].sum+=(ll)build(z*2+1,mid+1,r);
        return d[z].sum;
}

2.区间修改,区间查询

单点查询就相当于l=r的区间查询,就不太用单独写出来啦

区间修改

传递懒标记,从而优化复杂度

void add(int z,int l,int r)
{
    d[z].sum+=(ll)(r-l+1)*v;
    if(d[z].l==l&&d[z].r==r)
    {
        if(l!=r)
        d[z].p+=v;
        return ;
    }
    int mid=d[z].l+d[z].r>>1;
    if(l<=mid&&r>mid) 
    {
        add(z*2,l,mid);
     add(z*2+1,mid+1,r);
    }
    else
    {
        if(l>mid)
        add(z*2+1,l,r);
        else
        add(z*2,l,r);
    }

}

区间查询

简单查询操作

ll query(int z,int l,int r)
{
    if(l==d[z].l&&r==d[z].r) 
    {
        return d[z].sum;
    }
    ll sum=0;
    int mid=d[z].l+d[z].r>>1;
    if(d[z].p)
    {
        pushdown(z*2,d[z].p);
        pushdown(z*2+1,d[z].p);
        d[z].p=0;
    }
    if(l<=mid&&r>mid) 
    {
        sum+=(ll)query(z*2,l,mid);
     sum+=(ll)query(z*2+1,mid+1,r);
    }
    else
    {
        if(l>mid)
        sum+=(ll)query(z*2+1,l,r);
        else
        sum+=(ll)query(z*2,l,r);
    }

    return sum;
}

3.完整代码

代码如下:

#include<bits/stdc++.h>
#define bug(a) cout<<a<<endl;
#define bug2(a,b) cout<<a<<' '<<b<<endl;
#define bug3(a,b,c) cout<<a<<' '<<b<<' '<<c<<endl;

using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1e5+10;
int i,j,k,n,m,l,a[N],x,y,z,ans,v;
struct node
{
    int l,r;
    ll sum,p;
} d[N*4];
void pushdown(int z,int p)
{
    d[z].sum+=(ll)(d[z].r-d[z].l+1)*p;
    if(d[z].l!=d[z].r)
    d[z].p+=p;
}
ll build(int z,int l,int r)
{
        d[z].l=l,d[z].r=r;
        if(l==r) 
        {
            d[z].sum=a[l];
            return d[z].sum;
        }
        int mid=l+r>>1;
        d[z].sum+=(ll)build(z*2,l,mid);

        d[z].sum+=(ll)build(z*2+1,mid+1,r);
        return d[z].sum;
}
ll query(int z,int l,int r)
{
    if(l==d[z].l&&r==d[z].r) 
    {
        return d[z].sum;
    }
    ll sum=0;
    int mid=d[z].l+d[z].r>>1;
    if(d[z].p)
    {
        pushdown(z*2,d[z].p);
        pushdown(z*2+1,d[z].p);
        d[z].p=0;
    }
    if(l<=mid&&r>mid) 
    {
        sum+=(ll)query(z*2,l,mid);
     sum+=(ll)query(z*2+1,mid+1,r);
    }
    else
    {
        if(l>mid)
        sum+=(ll)query(z*2+1,l,r);
        else
        sum+=(ll)query(z*2,l,r);
    }

    return sum;
}
void add(int z,int l,int r)
{
    d[z].sum+=(ll)(r-l+1)*v;
    if(d[z].l==l&&d[z].r==r)
    {
        if(l!=r)
        d[z].p+=v;
        return ;
    }
    int mid=d[z].l+d[z].r>>1;
    if(l<=mid&&r>mid) 
    {
        add(z*2,l,mid);
     add(z*2+1,mid+1,r);
    }
    else
    {
        if(l>mid)
        add(z*2+1,l,r);
        else
        add(z*2,l,r);
    }

}
int main()
{

    cin>>n>>m;
    for(i=1;i<=n;i++)
    cin>>a[i];
    build(1,1,n);
    while(m--)
    {
        char s;
        cin>>s;
        if(s=='Q')
        {
            cin>>x>>y;
            ans=0;
            cout<<query(1,x,y)<<endl;
        }
        else
        {
            cin>>x>>y>>v;
            add(1,x,y);
        }
    }
    return 0;
}

总结

以上就是个人的线段树操作总结
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值