Segment Tree(线段树)

线段树是一种数据结构,适用于区间快速修改和查询。每个结点代表一个区间,存储方式为数组。线段树通过lazy标志处理延迟更新,需要额外空间。建树、更新和查询操作是线段树的主要功能。举例说明了区间加法修改和区间和查询的应用,如2020CCPC绵阳站J题。
摘要由CSDN通过智能技术生成

0.适用范围

线段树是一种数据结构,用于进行区间快速修改及查询。

 

1.先导知识

二叉树(略)

 

2.定义

线段树的每个结点代表着一个区间,我们将这棵树以数组的方式存储。

设一个下标为ID、代表区间[L,R]的结点,二分该结点,则该结点的左孩子(假如有的话)下标为2ID、代表区间[L,\frac{(L+R)}{2}];右孩子(假如有的话)下标为2ID+1、代表区间[\frac{(L+R)}{2}+1,R]

从线段树的定义中可以发现,需要开大约4倍空间(或者说是大于等于n的最小的2的正数次幂*2)

 

3.lazy标志

lazy标志表示该下标对于该lazy值已计入计算,但其子节点还未用这个lazy值进行更新

因此需要使用pushDown函数实现lazy标志的传递

void pushDown(int id)
{
    if(segTree[id].lazy)
    {
        int lid=id<<1,rid=id<<1|1;
        segTree[lid].lazy+=segTree[id].lazy;
        segTree[rid].lazy+=segTree[id].lazy;

        segTree[lid].val+=segTree[id].lazy*(segTree[lid].r-segTree[lid].l+1);
        segTree[rid].val+=segTree[id].lazy*(segTree[rid].r-segTree[rid].l+1);

        segTree[id].lazy=0;
    }
}//向下传递lazy

 

4.建树

按照定义建树即可

void pushUp(int id)
{
    segTree[id].val=segTree[id<<1].val+segTree[id<<1|1].val;
}//上推

void build(int l,int r,int id)
{
    segTree[id].l=l;
    segTree[id].r=r;
    if(l==r)
    {
        //-------------------------------------------------
        segTree[id].val=arr[l];
        //该区间是点,赋值为该点上的值
        //-------------------------------------------------
        return;
    }

    int m=(l+r)>>1;
    build(l,m,id<<1);
    build(m+1,r,id<<1|1);
    pushUp(id);
}//建树

 

5.更新

void apply(int id,ll add)
{
    //-------------------------------------------------
    //视题目情况做操作
    segTree[id].val+=add*(segTree[id].r-segTree[id].l+1);
    segTree[id].lazy+=add;
    //此处为区间加
    //-------------------------------------------------
}//更新时具体需要做的操作

void update(int l,int r,int id,ll add)
{
    //-------------------------------------------------
    if(r<segTree[id].l||l>segTree[id].r) return;
    //该区间与要更新的区间不相交
    //-------------------------------------------------


    if(l<=segTree[id].l&&r>=segTree[id].r)
    {
        //-------------------------------------------------
        apply(id,add);
        return;
        //该区间整个包含于要更新的区间
        //-------------------------------------------------
    }
    pushDown(id);
    update(l,r,id<<1,add);
    update(l,r,id<<1|1,add);
    pushUp(id);
}//更新

 

6.查询

ll query(int l,int r,int id)
{
    if(r<segTree[id].l||l>segTree[id].r) return 0;

    if(l<=segTree[id].l&&segTree[id].r<=r)
    {
        return segTree[id].val;
    }
    pushDown(id);

    ll ans=0;
    ans+=query(l,r,id<<1);
    ans+=query(l,r,id<<1|1);
    return ans;
}//查询

 

例题

区间加修改、区间和查询

#include<bits/stdc++.h>
using namespace std;

#define ll long long

typedef struct SegNode
{
    int l,r;
    ll lazy,val;
}segNode;

const int maxn=1e5;
ll arr[maxn+5];
segNode segTree[4*maxn+5];


void pushUp(int id)
{
    segTree[id].val=segTree[id<<1].val+segTree[id<<1|1].val;
}//上推

void pushDown(int id)
{
    if(segTree[id].lazy)
    {
        int lid=id<<1,rid=id<<1|1;
        segTree[lid].lazy+=segTree[id].lazy;
        segTree[rid].lazy+=segTree[id].lazy;

        segTree[lid].val+=segTree[id].lazy*(segTree[lid].r-segTree[lid].l+1);
        segTree[rid].val+=segTree[id].lazy*(segTree[rid].r-segTree[rid].l+1);

        segTree[id].lazy=0;
    }
}//向下传递lazy

void build(int l,int r,int id)
{
    segTree[id].l=l;
    segTree[id].r=r;
    if(l==r)
    {
        //-------------------------------------------------
        segTree[id].val=arr[l];
        //该区间是点,赋值为该点上的值
        //-------------------------------------------------
        return;
    }

    int m=(l+r)>>1;
    build(l,m,id<<1);
    build(m+1,r,id<<1|1);
    pushUp(id);
}//建树

void apply(int id,ll add)
{
    //-------------------------------------------------
    //视题目情况做操作
    segTree[id].val+=add*(segTree[id].r-segTree[id].l+1);
    segTree[id].lazy+=add;
    //此处为区间加
    //-------------------------------------------------
}//更新时具体需要做的操作

void update(int l,int r,int id,ll add)
{
    //-------------------------------------------------
    if(r<segTree[id].l||l>segTree[id].r) return;
    //该区间与要更新的区间不相交
    //-------------------------------------------------


    if(l<=segTree[id].l&&r>=segTree[id].r)
    {
        //-------------------------------------------------
        apply(id,add);
        return;
        //该区间整个包含于要更新的区间
        //-------------------------------------------------
    }
    pushDown(id);
    update(l,r,id<<1,add);
    update(l,r,id<<1|1,add);
    pushUp(id);
}//更新

ll query(int l,int r,int id)
{
    if(r<segTree[id].l||l>segTree[id].r) return 0;

    if(l<=segTree[id].l&&segTree[id].r<=r)
    {
        return segTree[id].val;
    }
    pushDown(id);

    ll ans=0;
    ans+=query(l,r,id<<1);
    ans+=query(l,r,id<<1|1);
    return ans;
}//查询

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
    build(1,n,1);
    while(m--)
    {
        int op,x,y;ll k;
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d%lld",&x,&y,&k);
            update(x,y,1,k);
        }
        else
        {
            scanf("%d%d",&x,&y);
            ll ans=query(x,y,1);
            cout<<ans<<endl;
        }
    }

    return 0;
}

2020CCPC绵阳站J题 Joy of Handcraft

调和级数趋近于log(类似于分块的思维)+线段树

#include<bits/stdc++.h>
using namespace std;

#define ll long long

typedef struct SegNode
{
    int l,r,lazy,val;
}segNode;

const int maxn=1e5;
int maxx[maxn+5];
segNode segTree[4*maxn+5];

void pushUp(int id)
{
    segTree[id].val=max(segTree[id<<1].val,segTree[id<<1|1].val);
}//上推

void pushDown(int id)
{
    if(segTree[id].lazy)
    {
        int lid=id<<1,rid=id<<1|1;
        segTree[lid].lazy=max(segTree[lid].lazy,segTree[id].lazy);
        segTree[rid].lazy=max(segTree[rid].lazy,segTree[id].lazy);

        segTree[lid].val=max(segTree[id].lazy,segTree[lid].val);
        segTree[rid].val=max(segTree[id].lazy,segTree[rid].val);

        segTree[id].lazy=0;
    }
}//向下传递lazy

void build(int l,int r,int id)
{
    segTree[id].l=l;
    segTree[id].r=r;
    if(l==r)
    {
        segTree[id].lazy=0;
        segTree[id].val=0;
        return;
    }

    int m=(l+r)>>1;
    build(l,m,id<<1);
    build(m+1,r,id<<1|1);
    pushUp(id);
}//建树

void apply(int id,int add)
{
    segTree[id].val=max(segTree[id].val,add);
    segTree[id].lazy=max(segTree[id].lazy,add);
}//更新时具体需要做的操作

void update(int l,int r,int id,int add)
{
    if(r<segTree[id].l||l>segTree[id].r) return;

    if(l<=segTree[id].l&&r>=segTree[id].r)
    {
        apply(id,add);
        return;
    }

    //pushDown(id);
    update(l,r,id<<1,add);
    update(l,r,id<<1|1,add);
    pushUp(id);
}//更新

int query(int l,int r,int id)
{
    if(r<segTree[id].l||l>segTree[id].r) return 0;

    if(l<=segTree[id].l&&segTree[id].r<=r)
    {
        return segTree[id].val;
    }
    pushDown(id);

    int ans=max(query(l,r,id<<1),query(l,r,id<<1|1));
    return ans;
}//查询

int main()
{
    int t;
    scanf("%d",&t);
    for(int tot=1;tot<=t;tot++)
    {
        memset(segTree,0,sizeof(segTree));
        memset(maxx,0,sizeof(maxx));
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            int ti,xi;
            scanf("%d%d",&ti,&xi);
            maxx[min(ti,m)]=max(maxx[min(ti,m)],xi);
        }
        build(1,m,1);
        for(int i=1;i<=m;i++)
        {
            int ti=i;
            int xi=maxx[ti];
            for(int j=1;j<=m;j+=2*ti)
            {
                update(j,j+ti-1,1,xi);
            }
        }
        printf("Case #%d:",tot);
        for(int i=1;i<=m;i++)
        {
            int ans=query(i,i,1);
            printf(" %d",ans);
        }
        printf("\n");
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值