线段树

Posted wzl19981116

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树相关的知识,希望对你有一定的参考价值。

一般先建立结构体,要开4倍,还有a数组,存一开始的数据

struct tree
{
    int l,r,sum;
}tr[N<<2];
int a[N];

建立一棵树

void built_tree(int x,int y,int i)
{
    tr[i].l =x;
    tr[i].r =y;
    if(x==y) tr[i].sum =a[x];
    else
    {
        int mid=(x+y)>>1;
        built_tree(x,mid,i<<1);//左树 
        built_tree(mid+1,y,i<<1|1);//右树 
        tr[i].sum =tr[i<<1].sum +tr[i<<1|1].sum; //和递归返回
    }
}

修改点的值,和查询

void update_tree(int q,int val,int i)
{
    if(tr[i].l ==q && tr[i].r ==q)
    tr[i].sum +=val;
    else
    {
        int mid=(tr[i].l+tr[i].r)>>1;
        if(q<=mid)
        update_tree(q,val,i<<1);
        else
        update_tree(q,val,i<<1|1);
        tr[i].sum =tr[i<<1].sum +tr[i<<1|1].sum; 
    }
}
int query_tree(int x,int y,int i)
{
    if(tr[i].l >=x&&tr[i].r <=y) return tr[i].sum ;
    else
    {
        int mid=(tr[i].l +tr[i].r )>>1;
        if(x>mid)
        return query_tree(x,y,i<<1|1);
        else if(y<=mid)
        return query_tree(x,y,i<<1);
        else
        return query_tree(x,y,i<<1)+query_tree(x,y,i<<1|1); 
    }
}

修改区间的值,查询

//重点详解:
//void pushdown(int);//下放惰性标记
//此处标记指的是惰性标记,实际上是让子节点暂时处于
//不更新状态等到用到的时候在做更新而区间加时要把和
//那个区间有关的区间全部价值,不然不配合输出
//超时了就要优化。
//你想啊,虽然x~y区间要增加一个值,难道这个区间就一
//定要用吗?
//如果区间值增加了但后来没有询问,我们一开始为什么
//要增加呢?
//正如背古文,如果考试不考,我们为什么要背呢?
//所以lazy思想就怎么产生了。
//lazy,就是懒嘛,就是先不被古文,等到考试要考了再
//去背嘛;
//先不增加区间,等到询问时在去增加嘛;
//我们可以搞一个add数组,专门把编号为num的区间要加
//的值记录下来
//如果要用了,再给线段树num加上v[num]的值,再把
//add[num]
//传给v[num*2]和v[num*2+1];然后v[num]清零
//“如果要用了”这一步用一个clean函数实现

//void buildtree(int,int,int);//构造线段树

//void update_tree(int,int,int,long long);//区间修改
//1,起始区间,终止区间,修改值

//long long query(int,int,int);//区间查询
//1,区间起始值,区间终止值

ll add[N<<2];//存惰性标记,也要四倍
void pushdown(int i)//下放惰性标记 
{
    ll lc=i<<1,rc=i<<1|1;
    tr[lc].sum +=(tr[lc].r -tr[lc].l +1)*add[i];
    tr[rc].sum +=(tr[rc].r -tr[rc].l +1 )*add[i];
    add[lc]+=add[i];
    add[rc]+=add[i];
    add[i]=0;
}
void update_tree(ll x,ll y,ll k,int i)
{
    ll lc=i<<1;
    ll rc=i<<1|1;
    if(tr[i].l>y||tr[i].r<x) return; //如果不属于,则返回
    if(x <= tr[i].l &&tr[i].r <=y)
    {
        tr[i].sum+=(tr[i].r -tr[i].l +1)*k;//加至此处不继续往下加 
        add[i]+=k;//存惰性标记 
    }else
    {
        if(add[i])
            pushdown(i);
        update_tree(x,y,k,lc);
        update_tree(x,y,k,rc);
        tr[i].sum =tr[lc].sum+tr[rc].sum; 
    } //查找 
}

ll query(ll x,ll y,int i)
{
    ll lc=i<<1,rc=i<<1|1;
    if(x <= tr[i].l && tr[i].r <= y) return tr[i].sum;
    if(x > tr[i].r|| y < tr[i].l) return 0;
    if(add[i])
        pushdown(i);
    return query(x,y,lc)+query(x,y,rc);
}






















以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章

线段树

CCF(除法):线段树区间修改(50分)+线段树点修改(100分)+线段树(100分)

线段树合并

数据结构——线段树

论线段树:二

线段树