吉司机线段树支持对区间的取
m
i
n
min
min和取
m
a
x
max
max操作。即以下两种操作:
1.
(
l
,
r
,
x
)
:
1. \ (l,\ r,\ x):
1. (l, r, x): 将
a
i
a_i
ai变为
m
i
n
(
a
i
,
x
)
min(a_i,\ x)
min(ai, x)。
2.
(
l
,
r
,
x
)
:
2. \ (l,\ r,\ x):
2. (l, r, x): 将
a
i
a_i
ai变为
m
a
x
(
a
i
,
x
)
max(a_i,\ x)
max(ai, x)。
如果是只有区间求和操作与以上取最值的操作,时间复杂度是
O
(
m
log
n
)
O(m\log n)
O(mlogn)的。
如果在加上区间修改操作,时间复杂度是
O
(
m
log
2
n
)
O(m\log^2 n)
O(mlog2n)的,但是实际运行效果更接近
O
(
m
log
n
)
O(m\log n)
O(mlogn)。
时间复杂度的证明由吉老师证过了。
例题
K. Peach Conference
一个长度为
n
n
n的序列,一开始序列中的每个数都是
0
0
0。
有
q
q
q次操作
(
m
,
l
,
r
)
(m,l,r)
(m,l,r)。
1.
m
=
0
1.\ m=0
1. m=0,输出区间
[
l
,
r
]
[l,r]
[l,r]的和。
2.
m
>
0
2.\ m>0
2. m>0,将区间
[
l
,
r
]
[l, r]
[l,r]中的每个数加上
m
m
m。
3.
m
<
0
3.\ m<0
3. m<0,将区间
[
l
,
r
]
[l,r]
[l,r]中的每个数加上
m
m
m,如果该区间中的某些数小于
0
0
0了,将其变为
0
0
0。
sol:
序列中的数有上下界的问题这一类问题可以用吉司机线段树解决,本题中每个数有一个下界
0
0
0。
我们可以正常的进行线段树的区间修改操作,但是每次对区间进行减法操作之后,我们在对该区间进行一次区间取
m
a
x
max
max操作,在本题中即对
0
0
0取
m
a
x
max
max。
code
#include <bits/stdc++.h>
#define len(x) int(x.size())
#define all(x) begin(x),end(x)
#define debug(x) cout<<#x<<": "<<x<<endl;
using namespace std;
using ll = long long;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
struct Node {
int l, r;
ll sum, add;
ll min, se, cnt; // 最小值,次小值,最小值个数
#define ls (u << 1)
#define rs (u << 1 | 1)
} tr[N * 4];
int n, m;
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum;
tr[u].min = min(tr[ls].min, tr[rs].min);
if (tr[ls].min == tr[rs].min) {
tr[u].se = min(tr[ls].se, tr[rs].se);
} else if (tr[ls].min < tr[rs].min) {
tr[u].se = min(tr[ls].se, tr[rs].min);
} else {
tr[u].se = min(tr[ls].min, tr[rs].se);
}
tr[u].cnt = 0;
if (tr[ls].min == tr[u].min) tr[u].cnt += tr[ls].cnt;
if (tr[rs].min == tr[u].min) tr[u].cnt += tr[rs].cnt;
}
void pushadd(int u, int v) {
tr[u].add += v;
tr[u].min += v;
tr[u].se += v;
tr[u].sum += 1ll * v * (tr[u].r - tr[u].l + 1);
}
void pushmin(int u, int v) { // ai -> max(ai, v)
if (tr[u].min >= v) return ;
tr[u].sum += 1ll * tr[u].cnt * (v - tr[u].min);
tr[u].min = v;
}
void pushdown(int u) {
if (tr[u].add) {
pushadd(ls, tr[u].add);
pushadd(rs, tr[u].add);
tr[u].add = 0;
}
pushmin(ls, tr[u].min);
pushmin(rs, tr[u].min);
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, 0, 0, INF, 1};
if (l == r) return ;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, int v) {
if (l <= tr[u].l and tr[u].r <= r) return pushadd(u, v);
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid) update(ls, l, r, v);
if (r > mid) update(rs, l, r, v);
pushup(u);
}
void chmax(int u, int l, int r, int v) { // ai -> max(ai, v)
if (tr[u].min >= v) return ;
if (l <= tr[u].l and tr[u].r <= r and tr[u].se > v)
return pushmin(u, v);
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid) chmax(ls, l, r, v);
if (r > mid) chmax(rs, l, r, v);
pushup(u);
}
ll query(int u, int l, int r) {
if (l <= tr[u].l and tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
ll res = 0;
if (l <= mid) res += query(ls, l, r);
if (r > mid) res += query(rs, l, r);
return res;
}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
cin >> n >> m;
build(1, 1, n);
while (m --) {
int opt, l, r;
cin >> opt >> l >> r;
if (opt == 0) cout << query(1, l, r) << '\n';
else {
update(1, l, r, opt);
if (opt < 0) chmax(1, l, r, 0);
}
}
return 0;
}