超详解线段树(浅显易懂,几乎涵盖所有线段树类型讲解,匠心之作,图文并茂)-CSDN博客
建树
void bui(int id,int l,int r)//创建线段树,id表示存储下标,区间[L,r]
{if(l == r)//左端点等于右端点,即为叶子节点(区间长度为1),直接赋值即可{tr[id] = a[l];return ;}
// 否则将当前区间中间拆开成两个区间int mid = (l + r) / 2;//mid则为中间点,左儿子的结点区间为[l,mid],右儿子的结点区间为[mid + 1,r]bui(id * 2,l,mid); //递归构造左儿子结点bui(id * 2 + 1,mid + 1,r); //递归构造右儿子结点
// 左右两个区间计算完成以后
// 合并到当前区间tr[id] = min(tr[id * 2],tr[id * 2 + 1]);//更新父节点
}
区间查询
//id 表示树节点编号,l r 表示这个节点所对应的区间
//x y表示查询的区间
int find(int id,int l,int r,int x,int y)
{//需要查询的区间[x,y]将当前区间[l,r]包含的时候if(x <= l && r <= y) return tr[id];int mid = (l + r) / 2,ans = -INT_MAX;// 如果需要查询左半区间if(x <= mid) ans = min(ans,find(id * 2,l,mid,x,y)); // 如果需要查询右半区间if(y > mid) ans = min(ans,find(id * 2 + 1,mid + 1,r,x,y));return ans;
}
单点修改
找到点,修改了往回走
返回的路上沿路修改父节点
// id 表示树节点编号,l r 表示这个节点所对应的区间
// 将 a[x] 修改为 v
// 线段树单点更新void gexi(int id, int l, int r, int x, int v)
{
// 找到长度为 1 的区间才返回if (l == r){//这一棵子树的值变成了v//这棵子树大小为1//最终肯定是要实现区间修改的tr[id] = v;return;}//否则找到 x 在左区间或者右区间去更新int mid = (l + r) / 2;//[l,mid]if (x <= mid) gexi(id * 2, l, mid, x, v);// 需要修改的值在左区间//[mid+1,r]else gexi(id * 2 + 1, mid + 1, r, x, v);// 需要修改的值在右区间//依然是以区间中的最大值为例tr[id] = max(tr[id * 2], tr[id * 2 + 1]);
}
区间修改
void push_up(int id)
{sumv[id] = sumv[id * 2] + sumv[id * 2 + 1];
}
void push_down(int id,int l,int r)
{if(lazy[id])//如果id有lazy标记{int mid = (l + r) / 2;//[l,r]中每个a[x]加上值lazy[id]//lazy是差分
//sumv是区间和,要求的//左区间和右区间都加上lazy[id * 2] += lazy[id];//将它的左孩子的lazy加上它的lazylazy[id * 2 + 1] += lazy[id];//将它的右孩子的lazy加上它的lazysumv[id * 2] += lazy[id] * (mid - l + 1);//左孩子的Q+=它下放的Q*区间长度sumv[id * 2 + 1] += lazy[id] * (r - mid);lazy[id] = 0;//清空lazy标记}
}//钱将更新
void qjgx(int id,int l,int r,int x,int y,int v)//id:目前查到的节点编号 目前区间为[l,r] 目标是讲[x,y]的所有数+v
{if(l >= x && r <= y)//[l,r]被[x,y]包含了{lazy[id] += v;//暂时不下放Q,加进lazy标记中sumv[id] += v * (r - l + 1);//将Q收入囊中return ;}push_down(id,l,r);//要来更新下面节点了,赶紧下放Qint mid = (l + r) / 2;if(x <= mid) qjgx(id * 2,l,mid,x,y,v);//因为只有x<=mid(即[l,mid]有一部分是被[x,y]覆盖了的)才需要去更新[l,mid]if(y > mid) qjgx(id * 2 + 1,mid + 1,r,x,y,v);push_up(id);//子节点更新完之后父节点当然也要更新(上升操作)
}