线段树

线段树又叫区间树,是一棵二叉树,其特点是能够快速的完成插入,更新,查找,统计工作,其时间复杂度为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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值