yp线段树

contents:

本质

二分的应用
前缀和
差分

模板1

点我?: 线段树小白.
只有单点修改和区间查询

1、更新一个点:
将子节点值合并
2、建树
建一棵树就是建左右子树
先从上至下
到达叶子
递归更新
3、修改一个点
修改整体到修改单点越来越细分,直到修改到一个值
最后值变了,也得更新
4、区间求和
相当于遍历完所有区间,如果遍历到的区间完全在分内那就加上,并且结束
和搜索是一样的

整体就是从上至下,再从下至上
#include <bits/stdc++.h>
using namespace std;
const int N = 1E5 + 7;
int sum[N << 2];
int arr[N], n;

//更新求和
void pushup(int rt)
{
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
//建立线段树
void build(int l, int r, int rt)
{
    if (l == r)
    {
        sum[rt] = arr[l];
        return;
    }
    int m = (l + r) >> 1;
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    pushup(rt);
}
//单点修改arr[L]+=C
void updata(int L, int C, int l, int r, int rt)
{
    if (l == r)
    {
        sum[rt] += C;
        return;
    }
    int mid = (l + r) >> 1;
    if (L <= mid)
        updata(L, C, l, mid, rt<<1);
    else
        updata(L, C, mid + 1, r, rt<<1|1);
    pushup(rt);
}
//区间求和
int query(int L, int R, int l, int r, int rt)
{
    if (L <= l && r <= R)
    {
        return sum[rt];
    }
    int ans = 0;
    int mid = (l + r) >> 1;
    if (L <= mid)
        ans += query(L, R, l, mid, rt<<1);
    if(R>mid)
        ans += query(L, R, mid + 1, r, rt<<1|1);
    return ans;
}

int main(){
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> arr[i];
    }
    build(1, n, 1);
    int m;
    cin >> m;
    while (m--)
    {
        int index, upvalue;
        cin >> index >> upvalue;
        updata(index, upvalue, 1, n, 1);
        int res = query(index, index, 1, n, 1);
        cout << res << endl;
    }
    system("pause");
    return 0;
}

5
1 2 3 4 5
5
1 2
3
2 3
5
3 1
4
5 2
7
1 2
5

模板2

点我!: 线段树本树.

求和

#include <bits/stdc++.h>
using namespace std;
#define maxn 100007 //元素总个数
int Sum[maxn << 2]; //Sum求和,开四倍空间
int add[maxn << 2];
int A[maxn], n; //存原数组下标[1,n]
//PushUp函数更新节点信息,这里是求和
void PushUp(int rt) { Sum[rt] = Sum[rt << 1] + Sum[rt << 1 | 1]; }
//Build函数建立线段树
void Build(int l, int r, int rt)
{ //[l,r]表示当前节点区间,rt表示当前节点的实际存储位置
    if (l == r)
    {                   //若到达叶节点
        Sum[rt] = A[l]; //存储A数组的值
        return;
    }
    int m = (l + r) >> 1;
    //左右递归
    Build(l, m, rt << 1);
    Build(m + 1, r, rt << 1 | 1);
    //更新信息
    PushUp(rt);
}
void Update(int L, int C, int l, int r, int rt)
{ //[l,r]表示当前区间,rt是当前节点编号//l,r表示当前节点区间,rt表示当前节点编号
    if (l == r)
    {                //到达叶节点,修改叶节点的值
        Sum[rt] = C; //+=,变值//=,修改值//
        return;
    }
    int m = (l + r) >> 1;
    //根据条件判断往左子树调用还是往右
    if (L <= m)
        Update(L, C, l, m, rt << 1);
    else
        Update(L, C, m + 1, r, rt << 1 | 1);
    PushUp(rt); //子节点的信息更新了,所以本节点也要更新信息
}
void Pushdown(int rt, int ln, int rn)
{
    if (add[rt])
    {
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        Sum[rt << 1] += ln * add[rt];
        Sum[rt << 1 | 1] += rn * add[rt];
        add[rt] = 0;
    }
}
void Update(int L, int R, int C, int l, int r, int rt)
{
    if (L <= l && r <= R)
    {
        Sum[rt] += (r - l + 1) * C;
        add[rt] += C;
        return;
    }
    int m = (l + r) >> 1;
    Pushdown(rt, m - l + 1, r - m);
    if (L <= m)
        Update(L, R, C, l, m, rt << 1);
    if (R > m)
        Update(L, R, C, m + 1, r, rt << 1 | 1);
    PushUp(rt);
}

int Query(int L, int R, int l, int r, int rt)
{ //[L,R]表示操作区间,[l,r]表示当前区间,rt:当前节点编号
    if (L <= l && r <= R)
    {
        //在区间内直接返回
        return Sum[rt];
    }
    int m = (l + r) >> 1;
    Pushdown(rt, m - l + 1, r - m);
    //左子区间:[l,m] 右子区间:[m+1,r]  求和区间:[L,R]
    //累加答案
    int ANS = 0;
    if (L <= m)
        ANS += Query(L, R, l, m, rt << 1); //左子区间与[L,R]有重叠,递归
    if (R > m)
        ANS += Query(L, R, m + 1, r, rt << 1 | 1); //右子区间与[L,R]有重叠,递归
    return ANS;
}

int main()
{
    cout << "n?" << endl;
    cin >> n;
    cout<<"n*arr[i]?"<<endl;
    for (int i = 1; i <= n; i++)
    {
        cin >> A[i];
    }
    Build(1, n, 1);
    cout << "m?" << endl;
    int m;
    cin >> m;
    while (m--)
    {
        int L,R, upvalue;
        cout << "L?R?V?" << endl;
        cin >> L>>R>> upvalue;
        Update(L,R, upvalue, 1, n, 1);
        cout << "L?R?" << endl;
        cin >> L >> R;
        int res = Query(L,R, 1, n, 1);
        cout << res << endl;
    }
    system("pause");
    return 0;
}

min

#include <bits/stdc++.h>
using namespace std;
#define maxn 100007 //元素总个数
int Sum[maxn << 2]; //Sum求和,开四倍空间
int add[maxn << 2];
int A[maxn], n; //存原数组下标[1,n]
//PushUp函数更新节点信息,这里是求和
void PushUp(int rt) { Sum[rt] = min(Sum[rt << 1], Sum[rt << 1 | 1]); }
//Build函数建立线段树
void Build(int l, int r, int rt)
{ //[l,r]表示当前节点区间,rt表示当前节点的实际存储位置
    if (l == r)
    {                   //若到达叶节点
        Sum[rt] = A[l]; //存储A数组的值
        return;
    }
    int m = (l + r) >> 1;
    //左右递归
    Build(l, m, rt << 1);
    Build(m + 1, r, rt << 1 | 1);
    //更新信息
    PushUp(rt);
}
void Update(int L, int C, int l, int r, int rt)
{ //[l,r]表示当前区间,rt是当前节点编号//l,r表示当前节点区间,rt表示当前节点编号
    if (l == r)
    {                //到达叶节点,修改叶节点的值
        Sum[rt] = C; //+=,变值//=,修改值//
        return;
    }
    int m = (l + r) >> 1;
    //根据条件判断往左子树调用还是往右
    if (L <= m)
        Update(L, C, l, m, rt << 1);
    else
        Update(L, C, m + 1, r, rt << 1 | 1);
    PushUp(rt); //子节点的信息更新了,所以本节点也要更新信息
}
void Pushdown(int rt, int ln, int rn)
{
    if (add[rt] != 0)
    {
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        Sum[rt << 1] += add[rt];
        Sum[rt << 1 | 1] += add[rt];
        add[rt] = 0;
    }
}
void Update(int L, int R, int C, int l, int r, int rt)
{
    if (L <= l && r <= R)
    { //在区间内
        Sum[rt] +=  C;
        add[rt] += C;
        return;
    }
    int m = (l + r) >> 1;
    Pushdown(rt, m - l + 1, r - m);
    if (L <= m)
        Update(L, R, C, l, m, rt << 1);
    if (R > m)
        Update(L, R, C, m + 1, r, rt << 1 | 1);
    PushUp(rt);
}

int Query(int L, int R, int l, int r, int rt)
{ //[L,R]表示操作区间,[l,r]表示当前区间,rt:当前节点编号
    if (L <= l && r <= R)
    {
        //在区间内直接返回
        return Sum[rt];
    }
    int m = (l + r) >> 1;
    Pushdown(rt, m - l + 1, r - m);
    //左子区间:[l,m] 右子区间:[m+1,r]  求和区间:[L,R]
    //累加答案
    int ANS = 0x3f3f3f3f;
    if (L <= m)
        ANS = min(ANS, Query(L, R, l, m, rt << 1)); //左子区间与[L,R]有重叠,递归
    if (R > m)
        ANS = min(ANS, Query(L, R, m + 1, r, rt << 1 | 1)); //右子区间与[L,R]有重叠,递归
    return ANS;
}

int main()
{
    cout << "n?" << endl;
    cin >> n;
    cout << "n*arr[i]?" << endl;
    for (int i = 1; i <= n; i++)
    {
        cin >> A[i];
    }
    Build(1, n, 1);
    cout << "m?" << endl;
    int m;
    cin >> m;
    while (m--)
    {
        int L, R, upvalue;
        cout << "L?R?V?" << endl;
        cin >> L >> R >> upvalue;
        Update(L, R, upvalue, 1, n, 1);
        cout << "L?R?" << endl;
        cin >> L >> R;
        int res = Query(L, R, 1, n, 1);
        cout << res << endl;
    }
    system("pause");
    return 0;
}

非递归

在这里插入代码片
上一篇:在RT-Thread上,移植STM32H7的USB时,遇到的小问题,记录一下


下一篇:RT-Thread 4.0.3 适配 UART_V2 版本