**
线段树(模板)
**
线段树是一个神奇的数据结构。
对于普通的线性结构对其进行区间上的修改复杂度为O(n),对于n特别大的情况或者修改特别多次数的话仍然会TLE,这个时候就应该使用树形数据结构,它会把单个修改的复杂度降为O(lgn)。
所以就需要线段树了。。
线段树可以实现单点修改,单点查询,区间修改,区间查询等功能。
树的定义
#define ls k<<1
#define rs k<<1|1
#define maxn 100008
struct h{
int l;
int r;
long long sum;
int lazy;
};
h a[maxn*4];//maxn为数据量,保险起见最好把数组开到4倍
建树
void build(int k,int l,int r){
a[k].l=l;
a[k].r=r;
a[k].lazy=0;//给延迟标记赋初值
if(l==r){
a[k].sum=sum[l];//sum为记录初始值的数组
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
update(k);
}
单点修改
void change(int k,int x,int y){
if(a[k].l==a[k].r){
a[k].sum+=y;
return ;
}
int mid=(a[k].l+a[k].r)>>1;
if(x<=mid)
change(ls,x,y);
else
change(rs,x,y);
update(k);
}
区间修改
void changeSegment(int k,int l,int r,int x)
{
if(a[k].l>=l&&a[k].r<=r)
{
a[k].m=(a[k].r-a[k].l+1)*x;
a[k].lazy=x;//根据需要确定是覆盖还是叠加,覆盖标记前要先将标记下传
return;
}
pushdown(k);//延迟标记下传
int mid=(a[k].l+a[k].r)>>1;
if(r>mid) changeSegment(rs,l,r,x);
if(l<=mid) changeSegment(ls,l,r,x);
update(k);
}
区间查询
long long query(int k,int l,int r){
if(a[k].l>=l&&a[k].r<=r)
return a[k].m;
pushdown(k); //先下传标记再查询
int mid=(a[k].l+a[k].r)>>1;
if(r<=mid)
return query(ls,l,r);
else if(l>mid)
return query(rs,l,r);
else
return query(rs,mid+1,r)+query(ls,l,mid);
}
更新
void update(int k){
a[k].m=a[ls].m+a[rs].m;
}
延迟标记下传
void pushdown(int k)
{
if(a[k].lazy){
a[ls].m=a[ls].m+(long long)a[k].lazy*(a[ls].r-a[ls].l+1);//根据需要确定操作
a[rs].m=a[rs].m+(long long)a[k].lazy*(a[rs].r-a[rs].l+1);
a[ls].lazy+=a[k].lazy;
a[rs].lazy+=a[k].lazy;
a[k].lazy=0;
}
}