线段树
线段树的建树,更新点、区间,查找点、区间
//左子树 u << 1, 右子树 u << 1 | 1
//建树或遍历核心是判断 mid 和 l,r 的关系
//是大区间包容小区间,优先遍历大区间
// lazy_tag 用作处理记录器,用到就处理,没用到只处理总节点,节省时间
//考虑最坏情况要开 N * 4 给线段树数组
//此代码演示区间最大值
//建树为一颗完全二叉树
//建树O(n) 其他操作一般为O(log n)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 100010;
int a[N]; //原数组
struct tree{
int l,r;
int v;
} t[N * 4];
//每次改变数值后都要调用更新树
void push_up(int k){
t[k].v = max(t[k << 1].v,t[k << 1 | 1].v);
}
//建树
void build(int u,int l,int r){
t[u] = {l,r};
if(l == r) t[u].v = a[l];
else{
int mid = l + r >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
push_up(u);
}
}
// O(logn) 更新点
void upd_point(int x,int v,int u){
if(t[u].l == x && t[u].r == x) t[u].v += v;
else{
int mid = t[u].l + t[u].r >> 1;
//判断在左子树还是右子树
if(x <= mid) upd_point(x,v,u << 1);
else upd_point(x,v,u << 1 | 1);
push_up(u);
}
}
//区间查询或更新要保持递归期间区间端点!!!不改变!!!
//改变的是递归左子树或者右子树
//区间查询
int query(int u,int l,int r){
if(l <= t[u].l && t[u].r <= r) return t[u].v;
//lazy_chk(u); 如果使用了lazy数组
int mid = t[u].l + t[u].r >> 1;
int res = 0;
if(l <= mid) res = query(u << 1,l,r);
if(mid < r) res = max(res,query(u << 1 | 1,l,r));
return res;
}
int lazy[N * 4];
//区间改变,利用lazy_tag减少时间损耗 nlogn -> logn
void lazy_chk(int u){
if(lazy[u]){
lazy[u << 1] += lazy[u];
lazy[u << 1 | 1] += lazy[u];
t[u << 1].v += lazy[u];
t[u << 1 | 1].v += lazy[u];
lazy[u] = 0;
}
}
// l <= mid 与左子树有交集
// mid < r 与右子树有交集
//有区间变化的区间查询
int query_lazy(int u,int l,int r){
if(l <= t[u].l && t[u].r <= r) return t[u].v;
lazy_chk(u); //如果使用了lazy数组
int mid = t[u].l + t[u].r >> 1;
int res = 0;
if(l <= mid) res = query_lazy(u << 1,l,r);
if(mid < r) res = max(res,query_lazy(u << 1 | 1,l,r));
return res;
}
//区间更新
void upd(int u,int v,int l,int r){
if(l <= t[u].l && t[u].r <= r) lazy[u] += v,t[u].v += v;
else{
lazy_chk(u); //检查要不要被lazy更新
int mid = t[u].l + t[u].r >> 1;
if(l <= mid) upd(u << 1,v,l,r);
if(mid < r) upd(u << 1 | 1,v,l,r);
push_up(u); //改变数据要更新树
}
}
int main(){
int n;
cin >> n;
for(int i = 1;i <= n;i ++) a[i] = i;
build(1,1,n);
for(int i = 1;i <= n;i ++) cout << query(1,i,i) << ' ';
cout << endl << "建树" << endl;
upd_point(2,2,1);
for(int i = 1;i <= n;i ++) cout << query(1,i,i) << ' ';
cout << endl << "更新点" << endl;
upd(1,1,1,n);
for(int i = 1;i <= n;i ++) cout << query_lazy(1,i,i) << ' ';
cout << endl << "更新区间" << endl;
}
cite:
1 2 3