线段树
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);
}
以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章