线段树模板

处理区间问题的利器——线段树,每个节点表示一段区间的一些信息,因为查询和更新都是从树顶往下推的所以没有必要把所管理的区间左右端点显示的储存在节点中,使用的时候用一个数组表示,是一棵完全二叉树所以

lc=o<<1
rc=o<<1|1

用位运算加快速度。

点更新

给定一个数列 a1,...,ai,...,an
有两个操作query ql,qr,求出这个区间的最大值,或者最小值,
update x v将A[x] 修改为v;
点更新


void update(int o,int L,int R,int p,int v)//p为修改点
{
   if(L == R)seg[o] = v;
   else{
        int M = (L+R)>>1;
    if(p<=M)update(2*o,L,M);else update(2*o+1,M+1,R);
    seg[o] = max(seg[2*o],seg[2*o+1]);
   }
}

区间查询

int query(int o,int L,int R,int ql,int qr)
{
    int ans = -INF;
    if(ql<=L && R<=qr)return seg[o];
    else{
        int M = (L+R)>>1;
        if(ql<=M)ans = max(ans,query(2*o,L,M));
        if(qr>M)ans = max(ans,query(2*o+1,M+1,R));
        return ans;
    }
}

建树
采用递归建树 O(nlgn) ,其他操作的建树操作也是一样的

void build(int o,int L,int R)
{
    if(L==R)
    {
        seg[o] = num[L];
    }else
    {
        int M = (L+R)>>1;
        build(2*o,L,M);
        build(2*o+1,M+1,R);
        seg[o] = max(seg[2*o],seg[2*o+1]);
    }
}

区间更新

给定一个数列 a1,...,ai,...,an
有两个操作query ql,qr,求出这个区间的最大值,或者最小值,还有和,
Add l, r x 对区间 Al,Al+1,...,Ar 都加上一个值 x
这个不同于点更新了,我们在其中使用了一个技巧,即只更新为一些不相交的区间的节点进行相加,这样一次更新可以保证在 O(lgn) 的时间内解决这个问题

void update(int o,int L,int R,int y1,int y2,int v)
{
    if(y1<=L && R<= y2)seg[o].addv+=v;
    else{
        int M = (L+R)>>1;
        if(y1<=M)update(o<<1,L,M);
        if(y2>M)update(o<<1|1,M+1,R);
    }
    maintain(o,L,R);
}

maintain

void maintain(int o,int L,int R)
{
    int lc = o<<1;int rc = o<<1+1;
    seg[o].sum = seg[o].min = seg[o].max = 0;
    if(Lmin = min(seg[o].min,seg[o].min);
        seg[o].max = max(seg[o].max,seg[o].max);
    }
    seg[o].min += seg[o].addv;seg[o].max+=seg[o].addv;
    seg[o].sum+=seg[o].addv*(R-L+1);
}

区间查询

int _sum,_min,_max;
//查询再统计累加值
void query(int o,int L,int R,int ql,int r,int addv)
{
    if(ql<=L && qr>=R)
    {
        _sum+=seg[o].sum+addv*(R-L+1);
        _min=min(_min,seg[o].min+seg[o].addv);
        _max=max(_max,seg[o].max+seg[o].addv);
    }else
    {
        int M = (L+R)>>1;
        if(ql<=M)query(o<<1,L,M,ql,qr,addv+seg[o].addv);
        if(qr>M)query((o<<1|1,M+1,R,ql,qr,addv+seg[o].addv);
    }
}

区间设值

l,r, 这之间的元素全部设置为v
下推与上推

void push_up(int o)
{
    seg[o].sum = seg[o<<1].sum+seg[o<<1|1].sum;
}

void push_down(int o,int L,int R)
{
    if(seg[o].setv)
    {
        int M = (L+R)>>1;
        int lc = o<<1;int rc = o<<1|1;
        seg[lc].setv = seg[rc].setv = seg[o].setv;
        seg[o].setv = 0;//清楚设值标记
        seg[lc].sum = seg[lc].setv*(M-L+1);
        seg[rc].sum = seg[rc].setv*(R-M);
    }
}

区间更新

void update(int o,int L,int R,int y1,int y2,int v)
{
    if(y1<=L && y2>=R)
    {
        seg[o].setv = v;
        seg[o].sum = v*(R-L+1);
    }else{
        push_down(o,L,R);
        int M = (L+R)>>1;
        if(y1<=M)update(o<<1,L,M,y1,y2,v);
        if(y2>M)update(o<<1|1,M+1,R,y1,y2,v);
        push_up(o);
    }

}

查询

int _sum;
void query(int o,int L,int R,int ql,int qr)
{
    if(seg[o].setv >= 0)
    {
        _sum+=seg[o].setv*(min(qr,R)-max(ql,L)+1);
        //_min = min(_min,seg[o].setv);
        //_max = max(_max,seg[o].setv);
    }else if(ql<=L && qr>=R)
    {
        _sum+=seg[o].sum;
        //_min = min(_min,seg[o].min);
       // _max = max(_max,seg[o].max);
    }else
    {
        int M = (L+R)>>1;
        if(ql<=M)query(o<<1,L,M,ql,qr);
        if(qr>M)query(o<<1|1,M+1,R,ql,qr);
    }
}

建树

void build(int o,int L,int R)
{
    seg[o].setv = 0;
    if(L == R)
    {
        seg[o].sum = 1;

    }else{
        int M = (L+R)>>1;
        int lc = o<<1;
        int rc = o<<1|1;
        build(lc,L,M);
        build(rc,M+1,R);
        seg[o].sum = seg[lc].sum+seg[rc].sum;
    }
}

你可能感兴趣的:(线段树,算法理论)