线段树(lazy算法+离散化)

本文详细介绍了线段树的基础知识,包括最简单的线段树问题和使用lazy算法优化的线段树。重点讲解了lazy算法在处理区间修改和查询上的应用,以及如何避免更新超时。同时,还探讨了离散化技术在节省空间方面的优势,通过实例展示了离散化的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

毕竟是写给自己看的
还是写好看一点吧

一、

最简单的

传送门 HDOJ 1754

题意~
给出N个数,两种操作:
1、U x y:修改第x个数的值为y;
2、Q x y:求第x到第y个的最大值,注:x未必比y小

标准的线段树对不对
这里写图片描述

我们可以理解成总裁管左右两个总经理,总经理管左右两个副总经理,副总经理管左右两个经理……每个管理者都只知道自己的值,不知道他们手下的值,所以他们每次都要问自己的手下,而他们的手下每次更新的时候都要反馈给上司……这样就很好理解了

#include<cstring>
#include<cstdio>
using namespace std;

int a[210000];//员工,只负责一开始表达自己的值

struct node //管理者(假设它是经理),管理者绝对不是员工,一定要分开来
//最底层的管理者只管一个员工,绝对不能想着:反正一个员工,就自己管自己算了。
//理解:有N个员工,就配有2N-1个管理者,用空间换时间
{
    int l,r,lc,rc,c;// 管理的员工编号是第l个到第r个(连续),lc表示左副经理的编号,rc表示右副经理是谁
    //c表示第l个员工至第r个员工的特征值:可以是和、最大值、或者最小值
}tr[410000]; int len;//tr数组就是管理者数组,len表示当前申请到第几个管理者

//到此,员工有自己的编号,管理者也有自己的编号

int mymax(int x,int y){  return x>y?x:y;} //求xy最大值的函数

void  bt(int l,int r) // build tree建立线段树,申请一个管理者,管理第l个员工至第r个员工
{
    len++; int now=len;// now记录当前管理者的编号
    tr[now].l=l; tr[now].r=r; tr[now].lc=tr[now].rc=-1;tr[now].c=0;//一开始当前管理者now的左右副总经理都是没人-1,管理者管理范围最大值为0,这里五个元素都要赋值
    if(l<r) //如果管的人大于1人,就有权申请两个副总经理帮忙管人
    {
        int mid=(l+r)/2; // mid为l和r的中间值,从中间分为两段[l,mid]和[mid+1,r]
        tr[now].lc=len+1; bt(    l  , mid  );  // [l,mid]给左副总管,让他先去管好[l,mid]
        tr[now].rc=len+1; bt( mid+1 ,  r   );  // [mid+1,r]给右副总管,让他先去管好[mid+1,r]
    }
}

void change(int now,int x,int k)//change的功能:在当前管理者now的管理范围内,把管第x个员工的管理者(不知道该管理者的编号)的值改为k
//理解:为什么第x个一定在now的管理范围内呢?
//注意:修改,改的是管理者,不是改员工,员工已经没用了。
{
    if( tr[now].l==tr[now].r) { tr[now].c=k;return ;}//如果now只管一人,那么这个人的编号一定是x,为什么?

    int lc= tr[now].lc, rc=tr[now].rc;//找出now的左右副总分别是谁
    int mid=( tr[now].l+ tr[now].r)/2;//找到now管理范围的中间位置
    if( x<=mid)          change(lc,x,k);  //如果x在now的左副总的管理范围,那么修改这件事就交给左副总去做
    else if( mid+1<=x)   change(rc,x,k);  //如果x在now的右副总的管理范围,那么修改这件事就交给右副总去做

    tr[now].c= mymax( tr[ lc ] .c  ,  tr[ rc ].c );//修改完后,注意要维护,有可能最大值发生变化了
}

int findmax(int now,int l,int r)//findmax的功能:在当前管理者now的管理范围内,找出第l个员工至第r个员工的最大值
{
    if( l== tr[now].l &&  tr[now].r== r) return tr[now].c;//如果now的管理范围刚好是[l,r],就不用问左右副总了
    int lc= tr[now].lc, rc=tr[now].rc;
    int mid=( tr[now].l+ tr[now].r)/2;
    if( r<=mid)         return  findmax(lc,l,r); //[l,r]在左副总的管理范围内
    else if( mid+1<=l)  return  findmax(rc,l,r); //[l,r]在右副总的管理范围内
    else  return mymax(  findmax(lc,l,mid)  ,  findmax(rc,mid+1,r)  );//其他情况就是[l,r]一部分在左副总,一部分在右副总
}
int main()
{
    int n,m,i,x,y; char ss[10];
    while( scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++) scanf("%d",&a[i]);
        len=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值