(以下内容多数出自notonlysuccess大神的线段树专辑)
首先是准备知识( 好吧, 我承认我是弱菜
- 满二叉树和完全二叉数中 非叶节点 id 的子节点序号分别为 id*2, id*2+1;(id<<1,id<<1|1)
在代码前先介绍一些我的线段树风格:
- maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
- lson和rson分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示
- 以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson和rson的预定义可以很方便
- PushUP(int rt)是把当前结点的信息更新到父结点
- PushDown(int rt)是把当前结点的信息更新给儿子结点
- rt表示当前子树的根(root),也就是当前所在的结点
一 单点更新 (最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上
- hdu1166 敌兵布阵
题意:O(-1)
思路:O(-1)
线段树功能:update:单点增减 query:区间求和 #include <cstdio> #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 /*上面真是一个超棒的风格呢*/ const int maxn = 55555; int sum[maxn<<2]; void PushUP(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; /*位运算运用, 尽量学起来吧*/ } void build(int l,int r,int rt) { if (l == r) { scanf("%d",&sum[rt]); /*原来输入是可以写在这里的啊*/ return ; } int m = (l + r) >> 1; build(lson); build(rson); PushUP(rt); } void update(int p,int add,int l,int r,int rt) { if (l == r) { sum[rt] += add; return ; } int m = (l + r) >> 1; if (p <= m) update(p , add , lson); else update(p , add , rson); PushUP(rt); } int query(int L,int R,int l,int r,int rt) { /*查询函数要注意的是返回的方式*/ if (L <= l && r <= R) { return sum[rt]; } int m = (l + r) >> 1; int ret = 0; if (L <= m) ret += query(L , R , lson); if (R > m) ret += query(L , R , rson); return ret; } int main() { int T , n; scanf("%d",&T); for (int cas = 1 ; cas <= T ; cas ++) { printf("Case %d:\n",cas); scanf("%d",&n); build(1 , n , 1); char op[10]; while (scanf("%s",op)) { if (op[0] == 'E') break; int a , b; scanf("%d%d",&a,&b); if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1)); else if (op[0] == 'S') update(a , -b , 1 , n , 1); else update(a , b , 1 , n , 1); } } return 0; }
- 关于线段树的基本概念一般可以从这题出来。树的结构靠递归维持,也就是说不需要传统的链式节点建树。
- hdu1754 I Hate It
题意:O(-1)
思路:O(-1)
线段树功能:update:单点替换 query:区间最值
#include <cstdio> #include <algorithm> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 222222; int MAX[maxn<<2]; void PushUP(int rt) { MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]); } void build(int l,int r,int rt) { if (l == r) { scanf("%d",&MAX[rt]); return ; } int m = (l + r) >> 1; build(lson); build(rson); PushUP(rt); } void update(int p,int sc,int l,int r,int rt) { if (l == r) { MAX[rt] = sc; return ; } int m = (l + r) >> 1; if (p <= m) update(p , sc , lson); else update(p , sc , rson); PushUP(rt); } int query(int L,int R,int l,int r,int rt) { if (L <= l && r <= R) { return MAX[rt]; } int m = (l + r) >> 1; int ret = 0; if (L <= m) ret = max(ret , query(L , R , lson)); if (R > m) ret = max(ret , query(L , R , rson)); return ret; } int main() { int n , m; while (~scanf("%d%d",&n,&m)) { build(1 , n , 1); while (m --) { char op[2]; int a , b; scanf("%s%d%d",op,&a,&b); if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1)); else update(a , b , 1 , n , 1); } } return 0; }
- 与上一题的代码相似度极高。
- 要注意的地方有:
- 结构变量表示方式与更新方式(本题中即pushup())根据需求变化
- 有时候甚至不需要建树, 或者说数组的初始状态已经是符合需求的初始状态!