线段树基本操作(1)
(建树,查询和单点修改)
用途
- 线段树可以快速的对一段区间进行操作,包括求区间最值,并在对某点修改后再次求区间最值,对一个区间上的所有点进行修改等
- 不需要对区间上的元素进行循环,而是一种类似于二分,分治的思想
方法
- 把一段长度为2^k的区间逐次对半分,可以总共分成2^(k+1)-1各节点,变成了一棵二叉树
- 对于区间[lt, rt],它的子节点为区间[lt, mid]和区间[mid+1, rt]
- 查询时只要要查询的区间的左右边界刚好等于已知区间的边界,就可以返回值了,不需要一搜到底
- 对于单点修改,基本上和二分查找差不多(查找时查找的值是该点的下标)
//线段树
using namespace std;
const int MAXN = 10000 + 10;
int n;
int val[2*MAXN], p[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 ;
}
int query(int node, int lt, int rt, int ll, int rr) //区间查询
//lt, rt:点node的左右区间 ll, rr:要查的左右区间
{
if(lt == ll && rt == rr) 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 min(v1, v2);
}
// 使用条件:相邻的区间的信息可以被合并成两个区间的并区间的信息
void change(int node, int lt, int rt, int u, int add) //单点修改
//u要修改的值的下标,add要给修改的值加上的值
{
if(lt == rt) {val[node] += add; return ;}
int mid = (lt + rt) >> 1;
if(u <= mid) change(node*2, lt, mid, u, add);
if(u > mid) change(node*2+1, mid+1, rt, u, add);
val[node] = min(val[node*2], val[node*2+1]); //回溯修改
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
cin >> p[i];
build(1, 1, n);
// int l, r;
// cin >> l >> r;
// cout << query(1, 1, n, l, r);
int u, d;
cin >> u >> d;
change(1, 1, n, u, d);
cout << query(1, 1, n, 2, 4);
return 0;
}