区间最值操作
区间最值操作,指的是对区间 [ l , r ] [l,r] [l,r] 里的全部数对 x x x 取 min \min min 或取 max \max max,即 a i = min ( a i , x ) a_i=\min(a_i,x) ai=min(ai,x) 或 a i = max ( a i , x ) a_i=\max(a_i,x) ai=max(ai,x)。
一道例题
HDU5306 Gorgeous Sequence
我们用线段树维护每个区间的最大值 m x mx mx 和严格次大值 s e se se,以及 m x mx mx 的个数 c n t cnt cnt。
考虑 1 1 1 操作,对于一个线段树上的区间,我们分类讨论一下:
- 如果 m x ≤ t : mx\leq t: mx≤t:
那么我们可以直接 r e t u r n return return - 如果 m x > t , s e < t : mx>t,se < t: mx>t,se<t:
我们在区间打一个 t a g tag tag 标记,并更新最大值为 t t t,同时更新 s u m = s u m − c n t ∗ ( m x − t ) sum = sum - cnt * (mx - t) sum=sum−cnt∗(mx−t) - 如果 s e ≥ t : se\ge t: se≥t:
因为此时我们并不知道哪些数大于 t t t,于是我们暴力递归子区间
这样子操作的复杂度看起来很玄学,但其实通过势能分析,可以推出复杂度大概是 O ( m l o g n ) O(mlogn) O(mlogn) 的,详情可参考吉老师 2016 2016 2016 年的国家集训队论文 (我实在不会啊QAQ
代码如下:
#include <bits/stdc++.h>
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
LL z = 1;
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10