线段树个人理解及模板

一.线段树的相关定义及用途

 (1)线段树的定义

    线段树是一种可以加快对区间进行更新以及查询的一种树状结构,类似于将一个区间的及其子区间的相关信息存储在一颗二叉树中。

(2)线段树的用途大致为以下几种

    1>某个子区间进行区间更新

    2>查询某个子区间的总和

    3>查询某个子区间的最值

二.线段树的建立

  (1)节点的内容(一个节点代表一个区间)

      1>NodeLeft-----该区间的左边界

      2>NodeRight-----该区间的右边界

      3>NodeMin--------该区间的最小值

      4>NodeMax------该区间的最大值

      5>NodeSum------该区间的总和

  (2)利用节点之间的下标表示它们所代表的区间之间的关系

         1>设父节点的编号为n,则其左半区间的编号为2*n,右半区间的编号为2*n+1

      2>设某个非总区间的编号为n,则其父区间编号为n/2

     

三.线段树的实例使用及代码

  (1)实例背景

    (2)建树

      1>将7个红包建立为一颗线段树

    

      2>建树代码

struct Tree {//定义结构
        ll l,r;//节点左右端点
        ll sum;//求和
        ll lazy;//延迟标记
        ll maxn;//最大值
        ll minn;// 最小值
} t[MAXN<<2];//开4倍空间

void push_up(ll rt) { //向上更新
    t[rt].sum = t[rt << 1].sum + t[rt << 1 | 1].sum;//更新和
    t[rt].maxn = max(t[rt << 1].maxn ,t[rt << 1 | 1].maxn);//更新最大值
    t[rt].minn = min(t[rt << 1].minn ,t[rt << 1 | 1].minn);//更新最小值
}

void build(ll l,ll r, ll rt) { //建树,rt==root
    t[rt].lazy = 0;
    t[rt].l=l;
    t[rt].r=r;
    if(l == r) {
        scanf("%lld",&t[rt].sum);//单个红包时输入金额
        t[rt].minn=t[rt].sum;
        t[rt].maxn=t[rt].sum;
        return;
    }
    ll mid = (l + r) >> 1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    push_up(rt);//向上更新
}

  (3)得到n~m的红包的金钱总额

 1 #define ll long long
 2 #define lson  rt << 1
 3 #define rson  rt << 1 | 1
 4
 5 void push_down(ll rt, ll m) {//pushdown函数
 6     if(t[rt].lazy) { //若有标记,则将标记向下移动一层
 7         t[rt << 1].lazy += t[rt].lazy;
 8         t[rt << 1 | 1].lazy += t[rt].lazy;
 9         t[rt << 1].sum += (m - (m >> 1)) * t[rt].lazy;
10         t[rt << 1 | 1].sum += (m >> 1) * t[rt].lazy;
11         t[rt << 1].minn += t[rt].lazy;
12         t[rt << 1 | 1].minn+= t[rt].lazy;
13         t[rt << 1].maxn += t[rt].lazy;
14         t[rt << 1 | 1].maxn+= t[rt].lazy;
15         t[rt].lazy = 0;//取消本层标记
16     }
17 }
18
19 ll query(ll L, ll R, ll rt) { //区间求和
20     if(L <= t[rt].l && R >= t[rt].r) {//如果当前节点所代表的区间包含于所求区间
21         return t[rt].sum;//直接返回sum
22     }
23     push_down(rt, t[rt].r - t[rt].l + 1);//向下更新,对lazy标记进行处理
24     ll mid = (t[rt].r + t[rt].l) >> 1;
25     ll ans = 0;
26     if(L <= mid) ans += query(L, R, lson);//分两边递归
27     if(R > mid) ans += query(L, R, rson);
28     return ans;
29 }

  (4)更新n~m的金钱数

void update(ll L,ll R, ll key, ll rt) { //区间更新
    if(L <= t[rt].l && R >= t[rt].r) {
        t[rt].sum+=(t[rt].r - t[rt].l + 1) * key;
        t[rt].minn+=key;
        t[rt].maxn+=key;
        t[rt].lazy+=key;
        return;
    }
    push_down(rt, t[rt].r - t[rt].l + 1);//向下更新
    ll mid = (t[rt].r + t[rt].l) >> 1;
    if(L <= mid) update(L, R, key, lson);
    if(R > mid) update(L, R, key, rson);
    push_up(rt);//向上更新
}

  (5)寻找最值

ll query_min(ll L, ll R, ll rt) { //区间求最小值
    if(L <= t[rt].l && R >= t[rt].r) {
        return t[rt].minn;
    }
    push_down(rt, t[rt].r - t[rt].l + 1);//向下更新
    ll mid = (t[rt].r + t[rt].l) >> 1;
    ll ans = 0x3f3f3f3f;
    if(L <= mid) ans = min(ans,query_min(L, R, lson));
    if(R > mid) ans =min(ans,query_min(L, R, rson)) ;
    return ans;
}

ll query_max(ll L, ll R, ll rt) { //区间求最大值
    if(L <= t[rt].l && R >= t[rt].r) {
        return t[rt].maxn;
    }
    push_down(rt, t[rt].r - t[rt].l + 1);//向下更新
    ll mid = (t[rt].r + t[rt].l) >> 1;
    ll ans = 0;
    if(L <= mid) ans = max(ans,query_max(L, R, lson));
    if(R > mid) ans = max(ans,query_max(L, R, rson));
    return ans;
}

原文地址:https://www.cnblogs.com/PokimonMaster/p/12208825.html

时间: 2024-11-05 14:53:15

线段树个人理解及模板的相关文章

线段树入门理解

在复习算法至分治法时,书本上主要介绍了合并排序和快速排序,较为简单.特拓展简单学习一个应用了分治法的算法结构--线段树. acm刷题时遇到许多连续区间的动态查询问题,例如求取某一区间上元素之和.求取某一区间上元素的最大值,此时如果使用一般的方法求解会使得时间超出要求.此时需要使用到线段树,其主要用于高效解决连续区间的动态查询问题. 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN),从而大大减少耗时

线段树详解及模板 (转载)

一步一步理解线段树 目录 一.概述 二.从一个例子理解线段树 创建线段树 线段树区间查询 单节点更新 区间更新 三.线段树实战 -------------------------- 一 概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)

(HDU 1698) Just a Hook(线段树)-附通用模板

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 37787    Accepted Submission(s): 18409 Problem Description In the game of DotA, Pudge’s meat hook is actually the most horrible thing

hdu1698 Just a Hook(线段树+区间修改+区间查询+模板)

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 54923    Accepted Submission(s): 25566 Problem Description In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of

【区间合并线段树】HDU 4509 模板

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int MAXN=100000+50; const int rt=0; vector<

HDU 1166 敌兵布阵(线段树更新单节点模板):

Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况.由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视.中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人

线段树(递归)模板

1 namespace Seg{ 2     struct segnode{ 3         int S,E,len,mid; 4         int Min, Tag; 5         segnode *L,*R; 6         segnode(int l,int r,int *base):S(l),E(r),len(r-l),mid(l+(len>>1)){ 7             Tag = 0; 8             if(len == 1){Min = b

线段树成段更新模板POJ3468 zkw以及lazy思想

别人跑几百毫秒 我跑 2500多 1 #include<cstdio> 2 #include<map> 3 //#include<bits/stdc++.h> 4 #include<vector> 5 #include<stack> 6 #include<iostream> 7 #include<algorithm> 8 #include<cstring> 9 #include<cmath> 10

线段树区间合并(模板)

poj3667 #include<cstdio> #include<algorithm> #define lid id << 1 #define rid id << 1 | 1 using namespace std; const int mx = 50010; struct tree{ int l, r; int ls, rs, ms; int lazy; }tree[mx<<2]; void build(int l, int r, int i