线段树的核心是区间 与query

pushup又命名更新 当子节点的数据发生了修改的时候就需要pushup 来更新当前节点 如build mofify 或者 用于重构一个答案节点quenry

pushdown 又名为延迟加和 传递 传递告诉子节点本操作做过的修改 当当前的节点的数据发生修改 或者之前修改了还没通知 下面的要使用数据的时候通知他 适用于modify query

pushup

每次pushup 只在最后代码段的时候使用 当维护的值不只是区间 还有其他附加的小心的时候就要使用 build modify query等
对于query维护的信息多的时候 直接返回节点node
有pushup(res, left, right);而不是直接pushup 是因为有可能 设计两个子区间共同的操作才可得到的上面的操作

modify

modify 不一定只是修改一边
如果传入的参数是lr区间的话
会使用两个if if进行修改而不是else if (if里面的条件还是一样的 )
注意在递归的时候if 的条件不能重复的 如l<=mid 和r>=mid 否则会麻烦的

query()

query如果返回的是node 那么则需要新建一个节点利用pushup更新节点 再返回
如果返回的是int 那么int 的一个变量 左右区间=+=一下 就直接返回int就可以了

https://www.acwing.com/activity/content/code/content/167568/#

需要查询的信息是 l,r区间里面的最大连续和
最大连续和由左右子节点l,r获得 =max(l最大字段和,r最大子段和,跨越中间的最大字段和(=左节点的右端最大字段和+右节点的左端最大字段和))
所以一个节点要存储的
l,r,lmax,rmax,ans
右因为 lmax的获得需要对应左右儿子节点的l.sum+r.left
所以还要维护一个sum

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 500010;

int n, m;
int w[N];
struct Node
{
    int l, r;
    int sum, lmax, rmax, tmax;
}tr[N * 4];

void pushup(Node &u, Node &l, Node &r)//直接传入本次要处理的节点和他的左右子节点 通过这样获得本次的节点信息
{
    u.sum = l.sum + r.sum;
    u.lmax = max(l.lmax, l.sum + r.lmax);
    u.rmax = max(r.rmax, r.sum + l.rmax);
    u.tmax = max(max(l.tmax, r.tmax), l.rmax + r.lmax);
}

void pushup(int u)
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if (l == r) 
    {
        tr[u] = {l, r, w[r], w[r], w[r], w[r]};
        return ;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
    

}

void modify(int u, int x, int v)
{
    if (tr[u].l == x && tr[u].r == x){
        
         tr[u] = {x, x, v, v, v, v};
         return ;
    }
    
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);
        pushup(u);
    
}

Node query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u];
    
    int mid = tr[u].l + tr[u].r >> 1;
        if (r <= mid) return query(u << 1, l, r);
        else if (l > mid) return query(u << 1 | 1, l, r);
        else
            {
                auto left = query(u << 1, l, r);
                auto right = query(u << 1 | 1, l, r);
                Node res;
                pushup(res, left, right);
                return res;
            }
    
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    build(1, 1, n);

    int k, x, y;
    while (m -- )
    {
        scanf("%d%d%d", &k, &x, &y);
        if (k == 1)
        {
            if (x > y) swap(x, y);
            printf("%d\n", query(1, x, y).tmax);//直接返回节点 然后输出节点的信息
        }
        else modify(1, x, y);
    }

    return 0;
}

区间最大公约数(差分数组为维护数组 )#

gcd(al, a2, a3…ar) = gcd(al, a2-a1, a3-a2 … ar - ar-1) = gcd(a1, gcd(b2, b3 … br))
=gcd(a[l],b[r]-b[l+1])=

对于差分数组为原数组的 query( 1, 1, l)表示 a[l]这个点的性质
对于query(1,l+1,r):
因为差分数组的性质 过程会出现负数 记得abs最终答案

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 500010;

int n, m;
LL w[N];
struct Node
{
    int l, r;
    LL sum, d;//sum表示差分数组 d表示最大公约数
}tr[N * 4];

LL gcd(LL a, LL b)
{
    return b ? gcd(b, a % b) : a;
}

void pushup(Node &u, Node &l, Node &r)
{
    u.sum = l.sum + r.sum;
    u.d = gcd(l.d, r.d);
}

void pushup(int u)
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        LL b = w[r] - w[r - 1];//差分数组
        tr[u] = {l, r, b, b};
    }
    else
    {
        tr[u].l = l, tr[u].r = r;
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void modify(int u, int x, LL v)
{
    if (tr[u].l == x && tr[u].r == x)
    {
        LL b = tr[u].sum + v;
        tr[u] = {x, x, b, b};
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);
        pushup(u);
    }
}

Node query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u];
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (r <= mid) return query(u << 1, l, r);
        else if (l > mid) return query(u << 1 | 1, l, r);
        else
        {
            auto left = query(u << 1, l, r);
            auto right = query(u << 1 | 1, l, r);
            Node res;
            pushup(res, left, right);
            return res;
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%lld", &w[i]);
    build(1, 1, n);

    int l, r;
    LL d;
    char op[2];
    while (m -- )
    {
        scanf("%s%d%d", op, &l, &r);
        if (*op == 'Q')
        {
            auto left = query(1, 1, l);
            Node right({0, 0, 0, 0});
            if (l + 1 <= r) right = query(1, l + 1, r);
            printf("%lld\n", abs(gcd(left.sum, right.d)));
        }
        else
        {
            scanf("%lld", &d);
            modify(1, l, d);
            if (r + 1 <= n) modify(1, r + 1, -d);
        }
    }

    return 0;
}


区间修改 懒标记

需要增加pushdown的操作 有的是modify query 均在处理完特殊情况后pushdown
然后modify区间的情况与query的情况类似了 能修改区间的最全表示尽量return

一个简单整数问题#

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define int long long 
typedef long long LL;

const int N = 100010;

int n, m;
int w[N];
struct Node
{
    int l, r;
    LL sum, add;
}tr[N * 4];

void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;//没有变化
}

void pushdown(int u)//懒标记的操作 对左右儿子操作 操作完清空标记
{
    auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    if (root.add)//如果有懒标记 传递给下面
    {
        left.add += root.add, left.sum += (LL)(left.r - left.l + 1) * root.add;
        right.add += root.add, right.sum += (LL)(right.r - right.l + 1) * root.add;
        root.add = 0;//懒标记恢复为0
    }
}

void build(int u, int l, int r)
{
     tr[u] = {l, r};
    if (l == r) {
          tr[u] = {l, r, w[r], 0}; 
          return ;
     }
    
        
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    
}

void modify(int u, int l, int r, int d)
{
    if (tr[u].l >= l && tr[u].r <= r)//修改这个区间相当于修改以这个点魏子节点的所有信息
    {
        tr[u].sum += (LL)(tr[u].r - tr[u].l + 1) * d;
        tr[u].add += d;
        return ;
    }
    else    // 一定要分裂 
    {
        pushdown(u);//一定 让子节点获得add信息 否则add为0 操作无效了
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, d);
        if (r > mid) modify(u << 1 | 1, l, r, d);
        pushup(u);//不只是维护区间信息需要pushup
    }
}

LL query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;

    pushdown(u);//需要传递给下面
    int mid = tr[u].l + tr[u].r >> 1;
    LL sum = 0;
    if (l <= mid) sum = query(u << 1, l, r);
    if (r > mid) sum += query(u << 1 | 1, l, r);
    return sum;
}


signed main()
{
    cin >> n>>m;

    for (int i = 1; i <= n; i ++ ) cin >> w[i];

    build(1, 1, n);

    char op[2];
    int l, r, d;

    while (m -- )
    {
        cin >> op>>l>>r;
        if (*op == 'C')
        {
            cin >> d;
            modify(1, l, r, d);
        }
        else cout << query(1, l, r)<<endl;
    }

    return 0;
}


维护序列https://www.acwing.com/problem/content/1279/#


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
#define int long long 
typedef long long LL;

const int N = 100010;

int n, p, m;
int w[N];
struct node
{
    int l, r;
    int sum, add, mul;
}tr[N * 4];

void pushup(int u)
{
    tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
}
void eval(node &t,int add,int mul){
    t.sum=((t.sum)*mul+(t.r-t.l+1)*add)%p;
    t.mul=t.mul*mul%p;
    t.add=(t.add*mul+add)%p;
}
void pushdown(int u){
    
    eval(tr[u<<1],tr[u].add,tr[u].mul);
    eval(tr[u<<1|1],tr[u].add,tr[u].mul);
    tr[u].add=0,tr[u].mul=1;
}
void build(int u,int l,int r){
    tr[u]={l,r,0,0,1};
    if(l==r){
        tr[u]={l,r,w[r],0,1};
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pushup(u);
}
void modify(int u,int l,int r,int add,int mul){
    if(l<=tr[u].l&&tr[u].r<=r){
        eval(tr[u],add,mul);
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,add,mul);
    if(r>mid) modify(u<<1|1,l,r,add,mul);
    
    pushup(u);
}
int query(int u,int l,int r){
    if(l<=tr[u].l&&tr[u].r<=r){
        return tr[u].sum;
    }
    pushdown(u);
    int mid =tr[u].l+tr[u].r>>1;
    int sum=0;
    if(l<=mid) sum=query(u<<1,l,r);
    if(r>mid) sum=(sum+query(u<<1|1,l,r))%p;
    return sum;
}
signed main()
{
    scanf("%d%d", &n, &p);
    for (int i = 1; i <= n; i ++ )
    cin>>w[i];
    build(1, 1, n);

    scanf("%d", &m);
    while (m -- )
    {
        int t, l, r, d;
        cin>>t>>l>>r;
        if (t == 1)
        {
            cin >> d;
            modify(1, l, r, 0, d);
        }
        else if (t == 2)
        {
            cin >> d;
            modify(1, l, r, d, 1);
        }
        else printf("%d\n", query(1, l, r));
    }

    return 0;
}


题目操作->转为树上思路练习

1.#

链接:https://ac.nowcoder.com/acm/contest/37160/G
一个整数 n,n 的初始值为 1,然后给出 q 次操作,每次操作格式如下:

  1. a 表示将 n 变为 n*a。
  2. l r 表示永久撤销第 l 次到第 r 次操作中的 1 操作。
  3. 输出 n 模 10^9 + 7的值。

线段树
将 q 次操作看成一个长度为 q 的序列,初始值都是 1 。
对于第 i 次操作,如果是操作 1 则将下标为 i 的位置修改为 a,
对于操作 2则进行区间l-r 修改为 1
对于操作 3 就是区间求乘积。

时间复杂度为O(nlog⁡n)

posted @   liang302  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示
主题色彩