众所周知,线段树是一种十分狗好用的算法。
线段树是通过一颗 每个节点都有两个儿子或一个儿子没有的二叉树中任何一个节点的左儿子的编号一定是这个节点的编号乘上2,右儿子则是再加1。
因此,将树的每一个节点定义为一段区间,里面维护自己想要的在区间中的值,如图所示
写线段树的第一步当然就是建造一颗这样的树,建树程序如下,时间复杂度(n log n)
void build(int ll,int rr,int number){
if(ll>rr)return;
tree[number].l=ll;tree[number].r=rr;
if(ll==rr)return;
int mid=(ll+rr)/2;
build(ll,mid,number*2);
build(mid+1,rr,number*2+1);
}
建树程序一般要根据题目来修改,但大致都是这样的
对于线段树的修改和查询,我们就以最简单的模板,也就是区间和修改为例(luogu p3372)
将一段区间的所有数加上x的代码如下
void in(int number,int ll,int rr,long long s){
if(!tree[number].l)return;
if(ll<=tree[number].l&&tree[number].r<=rr){
tree[number].value+=(tree[number].r-tree[number].l+1)*s;
tree[number].lazy+=s;
return;
}
else
{
if(tree[number].lazy)down(number);
if(ll<=(tree[number].l+tree[number].r)/2){
in(number*2,ll,rr,s);
}
if(rr>(tree[number].l+tree[number].r)/2){
in(number*2+1,ll,rr,s);