概念:
注意:s数组要开题目原数组的4倍大小。
s[p]这个点它代表着数组下标为l~r的元素的和或者最大最小值,这个l和r与数组结构无关,是人为定义构想的。
方法模板:
【对于s数组存储“区间和”时】
一、单点更新(初始化s数组)(单点查询和单点更新一样,只是当l==r达到叶子结点时直接返回s[p]即可)
1.单点加减更新
void modify(int p, int l, int r, int x, int v) { s[p] += v; //相当于在这里up操作 if (l == r) return; //叶结点则退出 int mid = (l + r) / 2; if (x <= mid) //判断x在左儿子还是右儿子 modify(p * 2, l, mid, x, v); else modify(p * 2 + 1, mid + 1, r, x, v); }
2.单点赋值更新
显然不能再像加减一样写,因为更新的叶节点的每个父节点具体不再是统一的加减操作。用上up函数,把儿子结点的信息更新到父节点。
void up(int p) { s[p] = s[p * 2] + s[p * 2 + 1]; } void modify(int p, int l, int r, int x, int v) { if (l == r) { s[p] = v; return; } int mid = (l + r) / 2; if (x <= mid) modify(p * 2, l, mid, x, v); else modify(p * 2 + 1, mid + 1, r, x, v); up(p); }
二、区间查询
int query(int p, int l, int r, int x, int y) { if (x <= l && r <= y) return s[p];//若该结点被查询区间包含 int mid = (l + r) / 2, res = 0; if (x <= mid) res += query(p * 2, l, mid, x, y); if (y > mid) res += query(p * 2 + 1, mid + 1, r, x, y); return res; }
只需结合以下两张图进行理解记忆:
【s数组存储区间最大or最小值】
一、单点更新(初始化s数组)(单点查询和单点更新一样,只是当l==r达到叶子结点时直接返回s[p]即可)
int renew(int p,int l,int r,int x,int v) //单点修改。注意s保存的是最大值 { if(l==r) return s[p]=v; int mid=(l+r)/2; if(x<=mid) return s[p]=max(s[p*2+1],renew(p*2,l,mid,x,v)); //如果要更新的点在左孩子,那就和右孩子的比较取最大,决定父节点值 else if(x>mid) return s[p]=max(s[p*2],renew(p*2+1,mid+1,r,x,v)); }
注意:返回值不再是void而是int,因为这里需要有左右孩子的比较。叶节点的返回处注意要赋上值。
二、区间查询
int getmax(int p,int l,int r,int x,int y) { if(x<=l && y>=r) return s[p]; int mid=(l+r)/2; int m=0; if(x<=mid) m=max(m,getmax(p*2,l,mid,x,y)); if(y>mid) m=max(m,getmax(p*2+1,mid+1,r,x,y)); return m; }
和树状数组的比较:
树状数组没有明确的二叉结构,有类似于前缀和的结构,长这样:
1.如果像之前国赛真题那道求“逆序对”的,确实还是应该离散化后用树状数组做比较好。
2.现在觉得如果是遇到那种存储“最大or最小值”的,树状数组没法做的,它只能存区间和,所以用线段树来做。
3.树状数组没有“单点赋值更新”的吧,都只是加减更新,所以遇到单点赋值更新还是用线段树。
例题(线段树存储区间最大值 模板题):
AC代码:
#include<iostream> #include<bits/stdc++.h> using namespace std; const int maxn=8e5+5; //注意数组开四倍哦!!!(题中写的:n<=200000) int s[maxn]; int renew(int p,int l,int r,int x,int v) //单点修改。注意s保存的是最大值 { if(l==r) return s[p]=v; int mid=(l+r)/2; if(x<=mid) return s[p]=max(s[p*2+1],renew(p*2,l,mid,x,v)); else if(x>mid) return s[p]=max(s[p*2],renew(p*2+1,mid+1,r,x,v)); } int getmax(int p,int l,int r,int x,int y) { if(x<=l && y>=r) return s[p]; int mid=(l+r)/2; int m=0; if(x<=mid) m=max(m,getmax(p*2,l,mid,x,y)); if(y>mid) m=max(m,getmax(p*2+1,mid+1,r,x,y)); return m; } int main() { int n,m; char a; int b,c; cin>>n>>m; for(int i=1;i<=n;i++) { scanf("%d",&b); renew(1,1,n,i,b); } for(int i=0;i<m;i++) { scanf(" %c",&a); if(a=='Q') { scanf("%d%d",&b,&c); printf("%d\n",getmax(1,1,n,b,c)); } else { scanf("%d%d",&b,&c); renew(1,1,n,b,c); } } return 0; }