线段树的作用
可以在 O ( log ( n ) ) O(\log(n)) O(log(n))时间复杂度内更新每一个点或者区间的值,并且可以在同样的时间复杂度里面查询区间的信息,信息包括区间最大值最小值,以及区间和等等
线段树的类型
- 单点更新
- 多点更新
//单点更新操作
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int n,m,k,a,b;
int sum[N<<2];
void pushup(int rt){
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void build(int rt, int l, int r){
if (l == r){
sum[rt] = 0;
return ;
}
int mid = l+r>>1;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
pushup(rt);
}
void update(int rt, int l, int r, int x, int y){
if(l == r){
sum[rt] += y;
return ;
}
int mid = l+r>>1;
if (x <= mid) update(rt<<1, l, mid, x, y);
else update(rt<<1|1,mid+1,r,x,y);
pushup(rt);
}
int query(int rt, int l, int r, int a, int b){
if (l == a && r == b){
return sum[rt];
}
int mid = l+r>>1;
if (b <= mid) return query(rt<<1, l, mid, a, b);
else if (a > mid) return query(rt<<1|1, mid+1, r, a, b);
else return query(rt<<1, l, mid, a, mid)+query(rt<<1|1, mid+1, r, mid+1, b);
}
signed main(){
cin >> n >> m;
build(1,1,n);
for (int i = 1; i <= m; i++){
scanf("%lld %lld %lld",&k,&a,&b);
if (k == 0){
update(1,1,n,a,b);
}else{
printf("%lld\n",query(1,1,n,a,b));
}
}
return 0;
}
//区间更新(lazy数组)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int n,q,a,b,c;
char opt;
int sum[N<<2],v[N],lazy[N<<2];
void pushup(int rt){
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int rt, int l, int mid, int r){
if (lazy[rt]){ //如果这个区间没有懒标记,直接不用做
lazy[rt<<1] += lazy[rt]; //左右儿子继承这个懒标记
lazy[rt<<1|1] += lazy[rt];
sum[rt<<1] += lazy[rt] * (mid-l+1); //左右儿子的sum加上自己区间长度乘以懒标记
sum[rt<<1|1] += lazy[rt] * (r-mid);
lazy[rt] = 0; //账还清了,清空懒标记
}
}
void build(int rt, int l, int r){
if(l == r){
sum[rt] = v[l];
return ;
}
int mid = (l+r)>>1;
build(rt<<1, l, mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void update(int rt, int l, int r, int x, int y, int c){
if (l == x && r == y){
lazy[rt] += c;
sum[rt] += c*(r-l+1);
return;
}
int mid = (r+l)>>1;
pushdown(rt,l,mid,r);
if (x > mid) update(rt<<1|1, mid+1, r, x, y, c);
else if(y <= mid) update(rt<<1, l, mid, x, y, c);
else update(rt<<1, l, mid, x, mid, c), update(rt<<1|1, mid+1, r, mid+1, y, c);
pushup(rt);
}
int query(int rt, int l, int r, int x, int y){
if (l == x && r == y) {
return sum[rt];
}
int mid = (l+r)>>1;
pushdown(rt, l, mid, r);
if (x > mid) return query(rt<<1|1, mid+1, r, x, y);
else if (y <= mid) return query(rt<<1, l, mid, x, y);
else return query(rt<<1, l, mid, x, mid) + query(rt<<1|1, mid+1, r, mid+1, y);
}
signed main(){
cin >> n >> q;
for (int i = 1; i <= n; i++) scanf("%lld",&v[i]);
build(1,1,n);
for (int i = 1; i <= q; i++){
cin >> opt;
if (opt == 'C'){
scanf("%lld %lld %lld",&a,&b,&c);
update(1,1,n,a,b,c);
}else{
scanf("%lld %lld",&a,&b);
printf("%lld\n",query(1,1,n,a,b));
}
}
return 0;
}
RMQ问题的解决方案
运用线段树的思想,从小区间推到大区间,两个区间求最小值。
#include<bits/stdc++.h>
//#include "stdc++.h"
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n, m, sum[N << 2],u,v;
int a[N];
void build(int rt, int l, int r){
if (l == r){
sum[rt] = a[l];
return ;
}
int mid = (l+r)>>1;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
sum[rt] = max(sum[rt<<1],sum[rt<<1|1]); //这里
}
void update(int rt, int l, int r, int x, int y){
if (l == r){
return ;
}
int mid = (l+r)>>1;
if(x <= mid) update(rt<<1,l,mid,x,y);
else update(rt<<1|1,mid+1,r,x,y);
sum[rt] = max(sum[rt<<1],sum[rt<<1|1]); //这里
}
int query(int rt, int l, int r, int x, int y){
if (l == x && r == y) return sum[rt];
int mid = (l+r)/2;
if (x > mid) return query(rt<<1|1,mid+1,r,x,y);
else if(y <= mid) return query(rt<<1, l, mid, x, y);
else return max(query(rt<<1, l, mid, x, mid), query(rt<<1|1, mid+1, r, mid+1, y)); //和这里
}
signed main(){
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1,1,n);
for (int i = 1; i <= m; i++) {
// cin >> u >> v;
scanf("%lld %lld",&u,&v);
cout << query(1,1,n,u,v) << "\n";
}
}