- 线段树基于分治思想的二叉树,用于区间信息统计
特点:
- 每个节点代表区间
- 唯一根节点,也就是全部区间
- 叶节点是长度为1的子区间,也就是所代表数组上的一个点
- 存线段树的结构体数组长度一般是 线段树所代表数组长度的4倍.
const int maxn = 1e3;
struct segmentTree {
int l,r;
int dat;
} e[4 * maxn];
- 建树
void build(int p,int l,int r) {
e[p].l = l; e[p].r = r;
if (l == r) {
e[p].dat = a[l];
return ;
}
int mid = (l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
e[p].dat = max(e[p*2].dat,e[p*2+1].dat);
}
- 修改某节点值
void change(int p,int index,int val) {
if (e[p].l == e[p].r) {
e[p].dat = val;
return ;
}
int mid = (e[p].l + e[p].r)/2;
if (index <= mid) change(p*2,index,val);
else change(p*2+1,index,val);
e[p].dat = max(e[p*2].dat,e[p*2+1].dat);
}
- 询问区间最值
int ask(int p,int l,int r) {
if (l<=e[p].l && e[p].r <= r) return e[p].dat;
int mid = (e[p].l + e[p].r)/2;
int val = -(1<<30);
if (l <= mid) val = max(val,ask(p*2,l,r));
if (r > mid) val = max(val,ask(p*2+1,l,r));
return val;
}
线段树例题
HDU 1166
#include <iostream>
#include <string.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn];
int n;
struct segmentTree {
int l,r;
int dat;
} e[4 * maxn];
void build(int p,int l,int r) {
e[p].l = l; e[p].r = r;
if (l == r) {
e[p].dat = a[l];
return ;
}
int mid = (l+r)/2;
build(p*2 , l , mid);
build(p*2+1,mid+1,r);
e[p].dat = e[p*2].dat + e[p*2+1].dat;
}
void change(int p,int index,int val) {
if (e[p].l == e[p].r) {
e[p].dat += val;
return ;
}
int mid = (e[p].l + e[p].r)/2;
if (index <= mid) change(p*2,index,val);
else change(p*2+1,index,val);
e[p].dat = e[p*2].dat + e[p*2+1].dat;
}
int ask(int p,int l,int r) {
if (l <= e[p].l && e[p].r <= r) return e[p].dat;
int mid = (e[p].l + e[p].r)/2;
int val = 0;
if (l <= mid) val += ask(p*2 ,l,r);
if (r > mid) val += ask(p*2+1,l,r);
return val;
}
int main() {
int cnt=0;
int times; scanf("%d",×);
while (times--) {
scanf("%d",&n);
memset(e,0,sizeof e);
memset(a,0,sizeof a);
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
build(1,1,n);
char ch[10];
printf("Case %d:\n",++cnt);
while (scanf("%s",ch) && ch[0]!='E') {
int l,r; scanf("%d%d",&l,&r);
if (ch[0] == 'Q')
printf("%d\n",ask(1,l,r));
else if (ch[0] == 'A')
change(1,l,r);
else if (ch[0] == 'S')
change(1,l,-r);
}
}
return 0;
}
- 2019/12/15 17:18:15
- 上面线段树写的这么挫,难怪线段树像白学了一样.一直手写不出来,这次重新写一下
- 区间修改+区间查询:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 4e5+10;
int a[maxn/4],left_[maxn],right_[maxn];
ll add[maxn],sum[maxn];
void pushup(int p) { sum[p] = sum[p<<1] + sum[p<<1|1]; }
void pushdown(int p) {
if (add[p]) {
sum[p<<1] += add[p]*(right_[p<<1] - left_[p<<1] + 1);
sum[p<<1|1] += add[p]*(right_[p<<1|1] - left_[p<<1|1] + 1);
add[p<<1] += add[p];
add[p<<1|1] += add[p];
add[p] = 0;
}
}
void build(int p,int l,int r) {
left_[p]=l,right_[p]=r;
if (l == r) {
sum[p] = a[l];
return ;
}
int mid = (l + r) >> 1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void change(int p,int l,int r,int val) {
if (l<=left_[p] && right_[p]<=r) {
sum[p] += (ll)val * (right_[p] - left_[p] + 1);
add[p] += val;
return ;
}
pushdown(p);
int mid = (left_[p] + right_[p]) >> 1;
if (l <= mid)
change(p<<1,l,r,val);
if (mid < r)
change(p<<1|1,l,r,val);
pushup(p);
}
ll query(int p,int l,int r) {
if (l<=left_[p] && right_[p]<=r)
return sum[p];
pushdown(p);
ll ans = 0;
int mid = (left_[p] + right_[p]) >> 1;
if (l <= mid)
ans += query(p<<1,l,r);
if (mid < r)
ans += query(p<<1|1,l,r);
return ans;
}
int n,m;
int main() {
[](){
::ios::sync_with_stdio(false);
::cin.tie(0);
::cout.tie(0);
}();
cin >> n >> m;
for (int i=1; i<=n; ++i)
cin >> a[i];
build(1,1,n);
while(m--) {
int index,x,y,k;
cin >> index;
if (index == 1) {
cin >> x >> y >> k;
change(1,x,y,k);
} else {
cin >> x >> y;
cout << query(1,x,y) << "\n";
}
}
return 0;
}