区间最值操作与历史最值问题(一)

本文详细介绍了如何使用SegmentTree Beats维护区间取最值操作,包括区间最值操作、区间历史最值的维护,以及支持区间加减操作的方法。通过实例解析和代码展示,解释了如何在不同场景下应用这些技术,如GorgeousSequence和EPhoneNetwork等题目。文章还探讨了将数域进行划分以支持更多操作,如在最假女选手问题中的应用。

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

前言

本文主要讲解一种叫做 S e g m e n t T r e e   B e a t s SegmentTree~Beats SegmentTree Beats 的维护区间取最值操作的问题,以及维护区间历史最值的方法。本文参考自许多博客,以及吉老师 2016 2016 2016 年的集训队论文,会加上很多例题进行讲解QAQ

区间最值操作

例题一 [HDU5306] Gorgeous Sequence

给出长度为 n ( n ≤ 1 e 6 ) n (n\le 1e6) n(n1e6) 的序列 { A n } \{A_n\} { An} m ( m ≤ 1 e 6 ) m(m\le1e6) m(m1e6) 次操作,每次操作为以下三种类型之一:
1.给出 l , r , k l,r,k l,r,k,对所有 i ∈ [ l , r ] i\in[l,r] i[l,r],将 A i A_i Ai 变成 m i n ( A i , k ) min(A_i,k) min(Ai,k)
2.给出 l , r l,r l,r,询问序列 A A A 在区间 [ l , r ] [l,r] [l,r] 的最大值
3.给出 l , r l,r l,r,询问序列 A A A 在区间 [ l , r ] [l,r] [l,r] 的和

在这道题中,第一种操作就叫区间最值操作。
我们把这种操作看作是一种标记,那怎么快速更新区间的信息呢?
可以发现,由于存在不同的值,所以区间的信息是无法更新的。但是如果只有一种值,区间取 m i n min min 就变得轻而易举了。
因此,可以想到一个看起来像暴力的做法。
我们用线段树维护每个区间的最大值 m x mx mx 和严格次大值 s e se se,以及 m x mx mx 的个数 c n t cnt cnt
考虑 1 1 1 操作,对于一个线段树上的区间,我们分类讨论一下:

  • 如果 m x ≤ k : mx\leq k: mxk:
    那么我们可以直接 r e t u r n return return
  • 如果 m x > k , s e < k : mx>k,se < k: mx>k,se<k:
    我们在区间打一个 t a g tag tag 标记,并更新最大值为 k k k,同时更新 s u m = s u m − c n t ∗ ( m x − k ) sum = sum - cnt * (mx - k) sum=sumcnt(mxk)
  • 如果 s e ≥ k : se\ge k: sek:
    因为此时我们并不知道哪些数大于 t t t,于是我们暴力递归子区间

这样子的操作看起来非常滴玄学,但是跑起来却非常滴快,轻松地就把这题 A A A 了。
吉老师通过势能分析得出这样子操作总的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn) 的。我们也可以换一种角度思考,假设会进行递归,那么一定存在一个节点,满足 m x > t , s e > t mx>t,se>t mx>t,se>t,那么通过这次取 m i n min min 之后, s e se se m x mx mx 都会变成 k k k。也就是说,值域会减少一。而线段树有 l o g n logn logn 层,每层的值域加起来是 O ( n ) O(n) O(n) 的,因此值域最多只有 O ( n l o g n ) O(nlogn) O(nlogn),所以总的复杂度是 O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn) 的(虽然看起来还是很玄学
为了方便理解,下面给出了这题区间打标记的代码:

void addtag(int rt, int c){
   
    if(mx[rt] <= c) return;
    sum[rt] -= cnt[rt] * (mx[rt] - c);
    mx[rt] = tag[rt] = c;//更新最大值并打标记
}
void pushdown(int rt){
   
    if(tag[rt]){
   
        addtag(lc, tag[rt]);
        addtag(rc, tag[rt]);
        tag[rt] = 0;
    }
}
void update(int l, int r, int rt, int a, int b, int c){
   
    if(mx[rt] <= c) return;
    if(l >= a && r <= b && se[rt] < c){
   //次大值小于 c,最大值大于 c,此时只修改最大值
        addtag(rt, c);
        return;
    }
    pushdown(rt);
    int m = l + r >> 1;
    if(a <= m) update(lson, a, b, c);
    if(b > m) update(rson, a, b
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值