线段树模板

本文提供线段树的两种模板代码,一种支持单点更新并维护区间最小值,另一种支持区间更新及区间和、最大最小值的维护。
摘要由CSDN通过智能技术生成

下面的两份线段树模板,一份是单点更新维护区间最小值,一份是区间更新,同时维护区间和,区间最大最小值。

代码如下:

///线段树单点更新
#include<iostream>
#include<cstdio>
#include<cstring>
#include<fstream>
using namespace std;

#define maxn 1000
#define INF 0x3f3f3f3f

int a[maxn];
int minv[maxn*4];   ///线段树标记数组

///线段树构造函数
///其实就是要找到相应线段在线段树中的编号
///然后记录一下信息(形参表示编号为o的线段树节点对应的线段是L--R)
void build(int o,int L,int R)
{
    int M=L+(R-L)/2;
    if(L==R)
        minv[o]=a[L];
    else
    {
        build(2*o,L,M);
        build(2*o+1,M+1,R);
        minv[o]=min(minv[2*o],minv[2*o+1]);
    }
}

///单点更新 更新a[p]的值为v
///先找到这个点在线段树中对应的编号,更新
///然后再递归向上更新
void update(int o,int L,int R,int p,int v)
{
    int M=L+(R-L)/2;
    if(L==R)
        minv[o]=v;
    else
    {
        if(p<=M)
            update(o*2,L,M,p,v);
        else
            update(o*2+1,M+1,R,p,v);
        minv[o]=min(minv[2*o],minv[2*o+1]);
    }
}

///区间查询 查询区间ql,qr之间的最小值
int query(int o,int L,int R,int ql,int qr)
{
    int ans=INF; int M=L+(R-L)/2;
    ///当前区间完全被待查询区间包含
    if(ql<=L&&qr>=R)
        return minv[o];
    if(ql<=M)  ///查询区间与左区间有交点
        ans=min(ans,query(o*2,L,M,ql,qr));
    if(qr>M)  ///查询区间与右区间有交点
        ans=min(ans,query(2*o+1,M+1,R,ql,qr));
    return ans;
}

int main()
{
    int n;
    ifstream fin("lkl.txt");
    while(fin>>n)
    {
        for(int i=1;i<=n;i++)
           fin>>a[i];
        build(1,1,n);
        cout<<query(1,1,n,2,5)<<endl;  ///查询a[1,n]中的最小值
        update(1,1,n,3,-1);   ///修改a[3]=-1
        cout<<query(1,1,n,2,5)<<endl; ///查询a[1,n]中的最小值
    }
  return 0;
}
///线段树区间更新加维护 区间最大值 最小值 区间和
#include<iostream>
#include<cstdio>
#include<cstring>
#include<fstream>
using namespace std;

#define INF 0x3f3f3f3f
#define maxn 1000

int a[maxn],sum[maxn*4],minv[maxn*4];
///每个节点都会被附加一个lazy标记
int lazy[maxn*4],maxv[maxn*4];

///更新子节点后维护父节点信息
void maintain(int o,int L,int R)
{
    int lc=2*o,rc=2*o+1;
    sum[o]=sum[lc]+sum[rc];
    minv[o]=min(minv[lc],minv[rc]);
    maxv[o]=max(maxv[lc],maxv[rc]);
}

///递归查询或者修改时向下传递信息
void pushdown(int o,int L,int R)
{
    int lc=2*o,rc=2*o+1,M=(L+R)/2;
    ///如果当前父节点有lazy标记的话就传递给它的儿子节点
    if(lazy[o]!=INF)
    {
        sum[lc]+=lazy[o]*(M-L+1);
        sum[rc]+=lazy[o]*(R-M);
        minv[lc]+=lazy[o];
        maxv[lc]+=lazy[o];
        minv[rc]+=lazy[o];
        maxv[rc]+=lazy[o];
        if(lazy[2*o]==INF)
            lazy[2*o]=lazy[o];
        else
            lazy[2*o]+=lazy[o];
        if(lazy[2*o+1]==INF)
            lazy[2*0+1]=lazy[o];
        else
            lazy[2*o+1]+=lazy[o];
        lazy[o]=INF;   ///传递完成后就将父节点的标记清除
    }
}

void build(int o,int L,int R)
{
     int M=L+(R-L)/2;
     if(L==R)
     {
         sum[o]=a[L];
         minv[o]=a[L];
         maxv[o]=a[L];
     }
     else
     {
         build(2*o,L,M);
         build(2*o+1,M+1,R);
         maintain(o,L,R);
     }
}

void update(int o,int L,int R,int ql,int qr,int add)
{
    int M=L+(R-L)/2;
    if(ql<=L&&qr>=R)
    {
        sum[o]+=(R-L+1)*add;
        minv[o]+=add;
        maxv[o]+=add;
        if(lazy[o]==INF)
            lazy[o]=add;
        else
            lazy[o]+=add;
        return ;
    }
    pushdown(o,L,R);
    if(ql<=M)
        update(o*2,L,M,ql,qr,add);
    if(qr>M)
        update(2*o+1,M+1,R,ql,qr,add);
    maintain(o,L,R);
}
///定义全局变量用来记录当前区间查询的结果
int _max,_min,_sum;
void query(int o,int L,int R,int ql,int qr)
{
    int M=L+(R-L)/2;
    if(ql<=L&&qr>=R)
    {
        _sum+=sum[o];
        _max=max(_max,maxv[o]);
        _min=min(_min,minv[o]);
        return ;
    }
    pushdown(o,L,R);
    if(ql<=M)
        query(2*o,L,M,ql,qr);
    if(qr>M)
        query(2*o+1,M+1,R,ql,qr);
}

void Init()
{
     _max=0,_min=INF,_sum=0;
}

void print(int x,int y)
{
    cout<<x<<" "<<y<<endl;
    cout<<_max<<" "<<_min<<" "<<_sum<<endl;
}

int main()
{
    int n;
    ifstream fin("lkl.txt");
    while(fin>>n)
    {
        memset(lazy,0x3f,sizeof(lazy));
        for(int i=1;i<=n;i++)
           fin>>a[i];
        build(1,1,n);
        Init();
        query(1,1,n,1,1);  ///查询a[1,n]中的最小值,最大值,区间和
        print(2,5);
        update(1,1,n,2,2,-1);   ///a[2,5]中的所有元素都减1
        Init();
        query(1,1,n,2,2); ///查询a[2,5]中的最小值,最大值,区间和
        print(2,5);
        Init();
        update(1,1,n,4,4,4); ///查询a[2,5]中的最小值,最大值,区间和
        query(1,1,n,4,4);
        print(2,5);
    }
 return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值