线段树基本操作(2)
区间修改
假如指定一个操作给一段区间的所有值加2,求任意区间的最小值
- 修改区间时依然要按照线段树的结点搜索,但是如果一直搜索到底的话复杂度过大
- 所以需要用到延迟标记
- 当我指定一段区间进行修改时,依然按照线段树查找区间的方法逐层查找,但是区别是当刚好找到某结点的左右值刚好等于当前要改的左右值时,不用再继续查找下去,只需要在这个节点上加个延迟标记
- 设要查找的区间为(ll, rr),已知区间为lt, rt
- 当(ll >= lt && rr <= rt)时,如果此时这个区间上有延迟标记,则将他加载现在区间的值上,然后把延迟标记释放给子节点,如果此时lt == ll && rr == rt,则返回当前结点的值即可
- 注意,如果一直查到了叶子结点,但这个叶子上没有延迟标记,也要返回该叶子节点的值
- 讨论完左右叶子节点后要更新当前节点的值
更新:才发现在change是就更改结点值而查询时只释放结点会更快,下面摘自网上
对于任意区间的修改,我们先按照查询的方式将其划分成线段树中的结点,然后修改这些结点的信息,并给这些结点标上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个结点p,并且决定考虑其子结点,那么我们就要看看结点p有没有标记,如果有,就要按照标记修改其子结点的信息,并且给子结点都标上相同的标记,同时消掉p的标记。
下面是代码
//线段树区间修改
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 10000 + 10;
int n;
int val[2*MAXN], p[MAXN];
int sign[2*MAXN]; //延迟标记
void build(int node, int lt, int rt) //建树
{
if(lt == rt) {val[node] = p[lt]; return ;}
int mid = (lt + rt) >> 1;
build(node*2, lt, mid); build(node*2+1, mid+1, rt);
val[node] = min(val[node*2], val[node*2+1]);
return ;
}
void change(int node, int lt, int rt, int ll, int rr) //区间修改
{
if(lt == ll && rr == rt) {sign[node]++; return ;}
int mid = (lt + rt) >> 1;
if(rr <= mid) {change(node*2, lt, mid, ll, rr); return ;}
if(ll > mid) {change(node*2+1, mid+1, rt, ll, rr); return ;}
change(node*2, lt, mid, ll, mid);
change(node*2+1, mid+1, rt, mid+1, rr);
}
int query(int node, int lt, int rt, int ll, int rr) //区间查询
{
if(ll >= lt && rr <= rt && sign[node])
{
val[node] += 2 * sign[node];
sign[node*2] += sign[node];
sign[node*2+1] += sign[node];
sign[node] = 0;
if(ll == lt && rr == rt) return val[node];
}
if(lt == rt) return val[node];
int mid = (lt + rt) >> 1;
if(rr <= mid) return query(node*2, lt, mid, ll, rr);
if(ll > mid) return query(node*2+1, mid+1, rt, ll, rr);
int v1 = query(node*2, lt, mid, ll, mid);
int v2 = query(node*2+1, mid+1, rt, mid+1, rr);
return val[node] = min(v1, v2);
}
int main()
{
freopen("tree.in", "r", stdin);
cin >> n;
for(int i = 1; i <= n; i++)
cin >> p[i];
build(1, 1, n);
change(1, 1, n, 2, 4);
int ans = query(1, 1, n, 1, n);
// cout << ans;
// for(int i = 1; i <= 2*n; i++)
// cout << sign[i] << " ";
// cout << endl;
for(int i = 1; i <= 2*n; i++)
cout << val[i] << " ";
}