线段树(Segment Tree)是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b]。因此线段 树是平衡二叉树。叶节点数目为N,即整个线段区间的长度。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。
如下图:
(1)采用静态方法构建线段树,即用数组来做,左子树为 2*i,右子树为 2*i+1。按照保守估计,数组空间一般开N*4,用到的空间实质上是N*2-1,但是考虑到 中间 会有一些空间没有使用,特别是最底层的。粗略地想,假设最底层只有最后两个 空间有用到,那么这一层前面的空余空间就约为N*2了。所以N*4是足够的。
(2)这部分一般来说涉及到下面几个函数:pushUp(由下往上更新修改后的数据), build(初始化线段树),update(修改叶子结点的数据),query(查询区间数据)。#include <stdio.h> #define N 500010 //数据范围 int sum[N << 2];//保存各个范围的sum值 //检查下层数据,更新当前的sum值 void pushup(int cur) // cur 表示当前数组的实际下标。 { sum[cur] = sum[cur << 1] + sum[cur << 1 | 1];//左右子树之和 } // [l, r] 表示当前数组存储的值的区间 void build_segTree(int left, int right, int cur) { if(left == right) //初始化线段树 { scanf("%d", &sum[cur]); return; } int m = (left+right) >> 1; build_segTree(left, m, cur<<1); build_segTree(m + 1, right, cur << 1 | 1); pushup(cur);//更新当前的sum } // 更新范围i的值,val 表示需要增加的值 void update(int i, int val, int left, int right, int cur) { //更新范围i的值,并向上传递sum if( left == right) { sum[cur] += val; return ; } int m = (left + right) >> 1; if(i <= m) update(i, val, left, m, cur << 1); else update(i, val, m+1, right, cur << 1 | 1); pushup(cur); //传递sum值 } //L 至 R 表示需要查询的区间 int query(int L, int R, int left, int right, int cur) { //当前节点l,r完全包含在查询区间L R内 if(L <= left && R >= right ) return sum[cur]; int result =0; int m = (left + right) >> 1; if(L <= m) result += query(L, R, left, m, cur << 1); if(R > m) result += query(L, R, m+1, right, cur << 1 | 1); return result; } int main() { //freopen("in.txt", "r", stdin); int t, n;//样例数,营地数 int ncase = 1; //当前例子编号 int v1, v2; scanf("%d", &t); while(t--) { scanf("%d", &n); build_segTree(1, n, 1);// 1,n 输入n个数 printf("Case %d:\n", ncase++); char qe[12]; while(scanf("%s", qe) != EOF) { if(qe[0] == 'E') break; scanf("%d %d", &v1, &v2); if(qe[0] == 'A') update(v1, v2, 1, n, 1); else if(qe[0] == 'S') update(v1, -v2, 1, n, 1); else if(qe[0] == 'Q') printf("%d\n", query(v1, v2, 1, n, 1)); } } return 0; }
#include <stdio.h> #include #define M 200005 int max[M<<2];//构建最高成绩线段树 void pushup(int cur) { //更新 max[cur] 的值 max[cur] = max[cur << 1] >= max[cur << 1 | 1] ? max[cur << 1] : max[cur << 1 | 1]; } void build(int l, int r, int cur) { if(l == r) //初始化线段树 { scanf("%d", &max[cur]); return ; } int m = (l+r)>>1; build(l, m, cur << 1); build(m+1, r, cur << 1 | 1); pushup(cur); } void update(int i, int val , int l, int r, int cur) { if(l == r) //更新 i 的值 为 val { max[cur] = val; return ; } int m = (l+r)>>1; if(i <= m) update(i, val, l, m, cur << 1); else update(i, val, m+1, r, cur << 1 |1); pushup(cur); } int query(int L, int R, int l, int r, int cur) { // //返回[L, R]的最高成绩 if(L<=l && R>=r) return max[cur]; int m=(l+r)>>1; int ma=0, mb=0; if(L <= m) ma = query(L, R, l, m, cur << 1); if(R > m) mb = query(L, R, m+1, r, cur << 1 |1); return ma>mb ? ma:mb; } int main() { freopen("in.txt", "r", stdin); int n,m;//人数,操作次数 char c; int a, b; while(EOF != scanf("%d %d", &n, &m)) { build(1, n, 1); while(m--) { getchar();// 注意把之前的换行符读取 scanf("%c %d %d", &c, &a, &b); if(c == 'U') update(a, b, 1, n, 1); else if(c == 'Q') printf("%d\n", query(a, b, 1, n, 1)); } } return 0; }