当前位置: 首页 > news >正文

数据结构与算法-数据结构-线段树1(单点修改,区间查询):最大数,你能回答这些问题吗

介绍:
概念

线段树是一种二叉树数据结构,它用于高效处理区间查询(如求区间和、区间最大值等)和单点修改、区间修改操作。线段树的每个节点代表一个区间,根节点代表整个待处理的区间,每个内部节点将其代表的区间分成两部分,分别由其左右子节点表示。

思想

线段树的核心思想是分治。它将一个大区间不断地分解成较小的区间,直到每个区间只包含一个元素。通过预先计算和存储这些小区间的信息,我们可以在查询时快速地合并这些信息,从而高效地处理大区间的查询请求。同时,对于修改操作,我们只需要更新涉及到的节点信息,而不需要遍历整个区间。

功能

线段树主要用于解决以下两类问题:

单点修改,区间查询:

在某个位置修改元素的值,并查询某个区间内元素的信息(如和、最大值、最小值等)。

typedef struct node {

    int l, r, M;

} node;

node tr[N * 4];

int pre = 0;

// 向上更新父节点信息

void pushup(int u) {

    tr[u].M = max(tr[u << 1].M, tr[u << 1 | 1].M);

}

// 构建线段树

void build(int u, int l, int r) {

    if (l == r) {

        tr[u] = {l, r, 0};

    } else {

        int mid = (l + r) >> 1;

        tr[u] = {l, r, 0};

        build(u << 1, l, mid);

        build(u << 1 | 1, mid + 1, r);

        pushup(u);

    }

}

// 区间查询

int query(int u, int l, int r) {

    if (l <= tr[u].l && tr[u].r <= r) {

        return tr[u].M;

    } else {

        int mid = (tr[u].l + tr[u].r) >> 1;

        int res = 0;

        if (mid >= l) {

            res = query(u << 1, l, r);

        }

        if (mid < r) {

            res = max(res, query(u << 1 | 1, l, r));

        }

        return res;

    }

    return 0;

}

// 单点修改

void modify(int u, int x, int v) {

    if (x == tr[u].l && tr[u].r == x) {

        tr[u].M = v;

    } else {

        int mid = (tr[u].l + tr[u].r) >> 1;

        if (mid >= x) modify(u << 1, x, v);

        else modify(u << 1 | 1, x, v);

        pushup(u);

    }

}

不光可以求这个max,还可以求sum等等

pushup 函数:用于更新父节点的信息,这里是取左右子节点的最大值。

build 函数:递归构建线段树,将整个区间不断地二分,直到每个区间只包含一个元素。

query 函数:递归查询指定区间的信息,如果当前节点的区间完全包含在查询区间内,则直接返回该节点的信息;否则,分别查询左右子节点,并合并结果。

modify 函数:递归修改指定位置的元素值,并更新涉及到的父节点信息。

区间修改,区间查询:

对某个区间内的所有元素进行统一修改,并查询某个区间内元素的信息。

对于区间修改,我们需要引入懒标记(Lazy Tag)来优化时间复杂度。懒标记的作用是在区间修改时,不立即更新所有受影响的节点,而是先将修改信息记录在父节点上,等到需要查询这些节点时再将标记下放到子节点。

typedef struct node {

    int l, r, M;

    int lazy; // 懒标记

} node;

node tr[N * 4];

int pre = 0;

// 向上更新父节点信息

void pushup(int u) {

    tr[u].M = max(tr[u << 1].M, tr[u << 1 | 1].M);

}

// 懒标记下放

void pushdown(int u) {

    if (tr[u].lazy) {

        tr[u << 1].M += tr[u].lazy;

        tr[u << 1].lazy += tr[u].lazy;

        tr[u << 1 | 1].M += tr[u].lazy;

        tr[u << 1 | 1].lazy += tr[u].lazy;

        tr[u].lazy = 0;

    }

}

// 构建线段树

void build(int u, int l, int r) {

    tr[u] = {l, r, 0, 0};

    if (l == r) {

        return;

    }

    int mid = (l + r) >> 1;

    build(u << 1, l, mid);

    build(u << 1 | 1, mid + 1, r);

    pushup(u);

}

// 区间查询

int query(int u, int l, int r) {

    if (l <= tr[u].l && tr[u].r <= r) {

        return tr[u].M;

    }

    pushdown(u);

    int mid = (tr[u].l + tr[u].r) >> 1;

    int res = 0;

    if (mid >= l) {

        res = query(u << 1, l, r);

    }

    if (mid < r) {

        res = max(res, query(u << 1 | 1, l, r));

    }

    return res;

}

// 区间修改

void modify(int u, int l, int r, int v) {

    if (l <= tr[u].l && tr[u].r <= r) {

        tr[u].M += v;

        tr[u].lazy += v;

        return;

    }

    pushdown(u);

    int mid = (tr[u].l + tr[u].r) >> 1;

    if (mid >= l) {

        modify(u << 1, l, r, v);

    }

    if (mid < r) {

        modify(u << 1 | 1, l, r, v);

    }

    pushup(u);

}

这里的懒标记可以不止一个,可以通过后面的题看出来


最大数:

题目描述

现在请求你维护一个数列,要求提供以下两种操作:

查询操作。

语法:Q L

功能:查询当前数列中末尾 L 个数中的最大的数,并输出这个数的值。

限制:L 不超过当前数列的长度。(L>0)

插入操作。

语法:A n

功能:将 n 加上 t,其中 t 是最近一次查询操作的答案(如果还未执行过查询操作,则 t=0),并将所得结果对一个固定的常数 D 取模,将所得答案插入到数列的末尾。

限制:n 是整数(可能为负数)并且在长整范围内。

注意:初始时数列是空的,没有一个数。

输入格式

第一行两个整数,M 和 D,其中 M 表示操作的个数,D 如上文中所述。

接下来的 M 行,每行一个字符串,描述一个具体的操作。语法如上文所述。

输出格式

对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。

分析:

一个涉及区间查询和单点修改的题目,可以尝试用树状数组or线段树来解决,下面是线段树的解法:

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=200010;

int n,p,m;

typedef struct node{

    int l,r,M;

}node;

node tr[N*4];

int pre=0;

void pushup(int u){

    tr[u].M=max(tr[u<<1].M,tr[u<<1|1].M);

}

void build(int u,int l,int r){

    if(l==r){

        tr[u]={l,r,0};

    }else {

        int mid=(l+r)>>1;

        tr[u]={l,r,0};

        build(u<<1,l,mid);

        build(u<<1 | 1,mid+1,r);

        pushup(u);

    }

}

int  query(int u,int l,int r){

    if(l<=tr[u].l&&tr[u].r<=r){

        return tr[u].M;

    }else {

        int mid =(tr[u].l+tr[u].r)>>1;

        int res=0;

        if(mid>=l){

            res=query(u<<1,l,r);

        }

        if(mid<r){

            res=max(res,query(u<<1|1,l,r));

        }

        return res;

    }

    return 0;

}

void modify(int u,int x,int v){

    if(x==tr[u].l&&tr[u].r==x){

        tr[u].M=v;

    }else{

        int mid =(tr[u].l+tr[u].r)>>1;

        if(mid>=x)modify(u<<1,x,v);

        else modify(u<<1|1,x,v);

        pushup(u);

    }

}

int  main(){

    scanf("%d%d",&m,&p);

    n=0;

    pre=0;

    build(1,1,m);

    while(m--){

        char op[2];

        int val;

        scanf("%s%d",op,&val);

        if(op[0]=='Q'){

            pre=query(1,n-val+1,n);

            cout<<pre<<endl;

        }else {

            int tmp=((val+pre)%p+p)%p;

            modify(1,n+1,tmp);

            n++;

        }

    }

}

你能回答这些问题吗

给定长度为 NN 的数列 AA,以及 MM 条指令,每条指令可能是以下两种之一:

1 x y,查询区间 [x,y][x,y] 中的最大连续子段和,即 maxx≤l≤r≤ymaxx≤l≤r≤y{∑i=lrA[i]∑i=lrA[i]}。

2 x y,把 A[x]A[x] 改成 yy。

对于每个查询指令,输出一个整数表示答案。

输入格式

第一行两个整数 N,MN,M。

第二行 NN 个整数 A[i]A[i]。

接下来 MM 行每行 33 个整数 k,x,yk,x,y,k=1k=1 表示查询(此时如果 x>yx>y,请交换 x,yx,y),k=2k=2 表示修改。

输出格式

对于每个查询指令输出一个整数表示答案。

每个答案占一行。

分析:


对于一个node来说,在l~r区间的一个最大的连续字段和可以是:在左儿子l中最大的连续字段和,在右儿子r中的最大的连续字段和,横跨左儿子和右儿子的连续字段和,针对这个设计出的node

node{

 int l,r;

 int maxc,lc,rc;

}

但是我们现在就要多的去维护lc和rc,考虑lc是这么得到的:左儿子的lc,左儿子的sum和右儿子的lc,这样就可以求出来lc了,那么rc同理

sum很好维护,这个线段树的模版

代码:

#include<bits/stdc++.h>

using namespace std;

int inf=-0x3f3f3f3f;

const int N=500010;

int n,m;

struct node{

    int l,r,maxc,lc,rc,sum;

}tr[N*4];

void pushup(int u){

    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;

    tr[u].lc=max(tr[u<<1].lc,tr[u<<1].sum+tr[u<<1|1].lc);

    tr[u].rc=max(tr[u<<1|1].rc,tr[u<<1|1].sum+tr[u<<1].rc);

    tr[u].maxc=max(tr[u<<1].maxc,max(tr[u<<1|1].maxc,tr[u<<1].rc+tr[u<<1|1].lc));

}

void build(int u,int l,int r){

    if(l==r){

        tr[u]={l,r,0,0,0,0};

    }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);

    }

}

node query(int u,int l,int r){

    int ll=tr[u].l,rr=tr[u].r;

    if(ll>=l&&rr<=r){

        return tr[u];

    }

    node res;

    int mid=(ll+rr)>>1;

    if(r<=mid){

        res=query(u<<1,l,r);

    }else if(l>mid){

        res=query(u<<1|1,l,r);

    }else {//一定要讨论清楚,

        node left=query(u<<1,l,r);

        node right=query(u<<1|1,l,r);

        res.sum=tr[u<<1].sum+tr[u<<1|1].sum;

        res.lc=max(left.lc,left.sum+right.lc);

        res.rc=max(right.rc,right.sum+left.rc);

        res.maxc=max(left.maxc,max(right.maxc,left.rc+right.lc));

    }

    return res;

}

void modify(int u,int x,int v){

    int ll=tr[u].l,rr=tr[u].r;

    if(ll==x&&rr==x){

        tr[u]={x,x,v,v,v,v};

    }else {

        int mid=(ll+rr)>>1;

        if(mid>=x)modify(u<<1,x,v);

        else modify(u<<1|1,x,v);

        pushup(u);

    }

}

int main(){

    scanf("%d%d",&n,&m);

    build(1,1,n+2);

    for(int i=1;i<=n;i++){

        int tmp;

        scanf("%d",&tmp);

        modify(1,i,tmp);

    }

    for(int i=0;i<m;i++){

        int k,x,y;

        scanf("%d%d%d",&k,&x,&y);

        if(k==1){

            if(x>y)swap(x,y);

            printf("%d\n",query(1,x,y).maxc);

        }else {

            modify(1,x,y);

        }

    }

}

相关文章:

  • XSS漏洞靶场
  • C++对象数组的使用
  • Web前端之UniApp、Taro、ReactNative和Flutter的区别
  • Python Django系列—多数据库
  • 信息集译码算法(ISD)
  • C++11QT复习 (五)
  • Lambda 和 Stream 从 0 到 1,从基础到实战
  • Go 语言当中的反射机制
  • 数据结构--二叉树--其一
  • 搭建k8s集群的可观测体系(log和metric)(已踩完坑)
  • 常用的几种思维方式
  • MySQL:DML
  • Node.js的安装和环境配置
  • ECharts实现数据可视化
  • QTcpSocket多线程连接慢问题
  • Axure RP 9.0教程: 基于动态面板的元件跟随来实现【音量滑块】
  • 面试总结2025
  • Transformers快速入门-学习笔记(二)
  • AQS的重入机制和锁释放逻辑
  • 知网打不开
  • 国外一个专门做配乐的网站/南京seo网络推广
  • 网站seo解决方案/东莞网站建设快速排名
  • python做网站吗/微信营销软件群发
  • 网站如何做延迟加载/重庆网站seo费用
  • 做网站找华企/百度一下百度搜索入口
  • 做网站需要注意的问题/软件开发网站