线段树又叫区间树,是一棵二叉树,其特点是能够快速的完成插入,更新,查找,统计工作,其时间复杂度为log(n),应用线段树的过程中,应用时考虑每个节点要维护的信息是什么,如何更新和查询。
线段树点修改 poj3264
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<string>
#include<set>
#include<stack>
#define ll long long
#define MAX 1000000
#define INF INT_MAX
#define eps 1e-6
#define REP(i,n) for (int i=0; i<(n); i++)
#define FOR(i,s,t) for (int i=(s); i<=(t); i++)
using namespace std;
struct cNode{ //要维护的节点信息
int root,L,R; //节点编号,对应区间起始位置
int minv,maxv;
}a[MAX]; //用数组记录线段树时,常需要开数据量的4倍大数组(有空间浪费)
void build(int root, int L, int R){ //建树,时间复杂度为O(n);
if (L == R){
a[root].L = L;
a[root].R = R;
a[root].minv = INF;
a[root].maxv = -INF;
return;
}
a[root].minv = INF;
a[root].maxv = -INF;
a[root].L = L;
a[root].R = R;
int M = L + (R-L) / 2;
build(root*2 + 1, L, M); //节点编号是从0开始时。某节点的左子节点的编号为2*root+1,右子节点的编号为2*root+2(二叉树的性质)
build(root*2 + 2, M + 1, R);
}
void insert(int root,int i,int v){ //将第i个元素的值置为v
if (a[root].L == a[root].R){
a[root].minv = a[root].maxv = v;
return;
}
a[root].minv = min(a[root].minv,v);
a[root].maxv = max(a[root].maxv,v);
int M = a[root].L + (a[root].R - a[root].L) / 2;
if (i <= M){
insert(root*2 + 1, i, v);
}
else{
insert(root*2 + 2, i, v);
}
}
int minV,maxV;
void query(int root,int s, int e){ //查询
if (a[root].minv >= minV && a[root].maxv <= maxV){ //剪枝
return;
}
if (a[root].L >= s && a[root].R <= e){ //如果某个节点代表的区间完全属于待分解区间(要查询的区间),则不再分解,返回信息
minV = min(minV, a[root].minv);
maxV = max(maxV, a[root].maxv);
return;
}
int M = a[root].L + (a[root].R - a[root].L) / 2; //区间分解的三种情况
if (e <= M){ //待分解的区间完全属于左子节点代表的区间时
query(root*2 + 1, s, e);
}
else if (s > M){ //待分解的区间完全属于右子节点代表的区间时
query(root*2 + 2, s, e);
}
else { //待分解的区间部分属于左子区间,部分属于右子区间时
query(root*2 + 1, s , M);
query(root*2 + 2, M + 1, e);
}
}
int main(){
int N,Q;
while (scanf("%d%d",&N,&Q) != EOF){
build(0,1,N);
int t;
for (int i=1; i<=N; i++){
scanf("%d",&t);
insert(0,i,t);
}
int u,v;
for (int i=1; i<=Q; i++){
minV = INF;
maxV = -INF;
scanf("%d%d",&u,&v);
query(0,u,v);
printf("%d\n",maxV - minV);
}
}
return 0;
}
线段树区间修改 poj3468
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<queue>
#include<set>
#include<string>
#include<stack>
#define ll long long
#define MAX 500000
#define INF INT_MAX
#define eps 1e-6
#define REP(i,n) for (int i=0; i<(n); i++)
#define FOR(i,s,t) for (int i=(s); i<=(t); i++)
using namespace std;
struct cNode{ //确定节点信息
int root,L,R;
ll sum,inc; //sum记录的不一定是该区间内所有元素的和,inc记录该区间中所有元素的增量
}a[MAX];
void build(int root, int L, int R){ //建树
a[root].L = L;
a[root].R = R;
a[root].inc = 0;
a[root].sum = 0;
if (L != R){
int M = L + (R - L) / 2;
build(root*2 + 1, L, M);
build(root*2 + 2, M + 1, R);
}
}
void insert(int root,int i, int v){ //将元素i的值增加v
if (a[root].L == a[root].R){
a[root].sum += v;
return;
}
a[root].sum += v;
int M = a[root].L + (a[root].R - a[root].L) / 2;
if (i <= M){
insert(root*2 + 1, i, v);
}
else{
insert(root*2 + 2, i, v);
}
}
void update(int root, int s,int e, int v){ //将区间[s,e]内所有元素的值增加v
if (a[root].L == s && a[root].R == e){ //当待分解的区间[s,e]正好覆盖一个节点时才更新
a[root].inc += v;
return;
}
a[root].sum += v * (e-s+1); //如果待分解的区间不能正好覆盖一个节点,要更新sum(加上本次增量v),再将增量往下带
int M = a[root].L + (a[root].R - a[root].L) / 2;
if (e <= M){
update(root*2 + 1, s, e, v);
}
else if (s > M){
update(root*2 + 2, s, e, v);
}
else{
update(root*2 + 1 , s, M, v);
update(root*2 + 2, M + 1, e, v);
}
}
ll sumv;
void query(int root, int s, int e){
if (a[root].L == s && a[root].R == e){ //待分解区间正好覆盖(注意:此处是正好覆盖,上题中式包含,思考?)一个节点时,不在分解,返回区间和
sumv += a[root].sum + a[root].inc * (e - s + 1); //注意区间和由是sum和inc共同决定的
return;
}
if (a[root].L != a[root].R){ //待分解的区间不能恰好覆盖一个节点时,如果节点root不是叶子节点,将节点的inc往下带(更新root节点sum,将root节点inc清0)
a[root*2 + 1].inc += a[root].inc;
a[root*2 + 2].inc += a[root].inc;
}
a[root].sum += a[root].inc * (a[root].R - a[root].L + 1);
a[root].inc = 0;
int M = a[root].L + (a[root].R - a[root].L) / 2;
if (e <= M){
query(root*2 + 1 , s, e);
}
else if ( s > M){
query(root*2 + 2, s, e);
}
else{
query(root*2 + 1, s , M);
query(root*2 + 2, M + 1, e);
}
}
int main(){
int N,Q;
char t[10];
while (scanf("%d%d",&N,&Q) != EOF){
build(0,1,N);
int temp;
for (int i=1; i<=N; i++){
scanf("%d",&temp);
insert(0,i,temp);
}
int u,v,w;
for (int i=1; i<=Q; i++){
scanf("%s",t);
if (t[0] == 'Q'){
scanf("%d%d",&u,&v);
sumv = 0;
query(0,u,v);
printf("%lld\n",sumv);
}
else{
scanf("%d%d%d",&u,&v,&w);
update(0,u,v,w);
}
}
}
return 0;
}