对于修改,看上去要用 Segment Tree Beats
维护。
查询根据经典套路,维护每个结点的最大前缀和最大后缀。
我们知道 Segment Tree Beats
的思想是仅处理仅会修改最小值的区间,其他的暴力递归,不过增加最小值对答案的影响是什么呢?乍一看最大前缀的区间不会修改,但是仔细考虑后会发现:[3, -1, 2, -1],如果将 -1 全部改为 1,那么最大前缀的右端点会右移, 同理最大后缀的左端点会左移。
观察后会发现,最大前缀右移是因为有些前缀修改前的和较小但其包含了许多的最小值,当最小值增加 \(\Delta\) 时它的和的增量非常大。容易发现存在一个阈值 \(\max\Delta\),当答案小于这个数的时候区间一定不会变动。
假设有一个前缀被我们认为是“可能的未来最大前缀”,设其和为 \(s'\), 包含了最小值 \(cnt'\) 次,而当前和维 \(s\),包含最小值 \(cnt\) 次,那么要使前者转移后者,就需要满足 \(s < s' + (cnt' - cnt) \Delta\), 移项后得到
\(\dfrac{s - s'}{cnt'-cnt} < \Delta\)。
问题是,有可能存在很多的这样区间。考虑交给左右儿子分别处理。如果先令 \(\max\Delta\gets\min\{\max\Delta_l,\max\Delta_r\}\),那么不难发现,若 \(\Delta<\max\Delta\),那么左右儿子的最大前缀区间都不会改变。此时的“可能未来最大前缀”就只有一个,就是当实际最大前缀是左儿子的最大前缀时,右儿子最大前缀加上左儿子的全部区间。
由于我们只对区间取 \(\max\),固最大前缀等只会不断右移,所以复杂度是正确的,每个节点只会额外的重构操作一次。
还有一个小细节需要注意:如果有多个最大前缀区间可以选择,选择最长的。如果选择最短的可能会导致不断的右移导致复杂度错误。
经过势能分析,就能得出时间复杂度为 \(O(n\log^2n)\)。
#include <iostream>
#include <cstdio>
#define int long long
static const int N = 100005, inf = 1e9;
template<typename T>
void read(T &x) {
x = 0; bool f = false; int ch = getchar();
for (; !isdigit(ch); ch = getchar()) f ^= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
x = f ? ~x + 1 : x;
}
template<typename T, typename ...Args>
void read(T &x, Args &...args) {read(x); read(args...);}
void cmin(int &x, int y) {if (y < x) x = y;}
void cmax(int &x, int y) {if (y > x) x = y;}
int n, q, a[N];
struct SegmentTree {
#define lson (x << 1)
#define rson ((x << 1) | 1)
struct que {
int val, cnt;
que(int x = -inf, int v = 0): val(x), cnt(v) {}
friend bool operator < (que x, que y) {return x.val < y.val;}
friend que operator + (que x, que y) {return que(x.val + y.val, x.cnt + y.cnt);}
void add(int x) {val += x * cnt;}
};
struct TreeNode {
int min, minimin, max, mintime;
que sum, pre, suf, len;
void clear() {
min = minimin = mintime = inf;
max = -inf;
sum = pre = suf = len = que(0, 0);
}
void set(int x) {
min = max = x;
minimin = mintime = inf;
sum = pre = suf = len = que(x, 1);
}
void upmin(int x) {
if (min == x) return;
min = x;
sum.cnt = pre.cnt = suf.cnt = len.cnt = 0;
}
} t[N << 2];
int calc(que pre, que suf) {
if (suf.cnt <= pre.cnt) return inf;
return (pre.val - suf.val) / (suf.cnt - pre.cnt);
}
void pushup(TreeNode &x, TreeNode ls, TreeNode rs) {
x.min = std::min(ls.min, rs.min), x.minimin = std::min(ls.minimin, rs.minimin);
if (ls.min < rs.min) cmin(x.minimin, rs.min);
if (rs.min < ls.min) cmin(x.minimin, ls.min);
ls.upmin(x.min), rs.upmin(x.min);
x.sum = ls.sum + rs.sum, x.len = std::max(std::max(ls.len, rs.len), ls.suf + rs.pre);
x.pre = std::max(ls.pre, ls.sum + rs.pre), x.suf = std::max(rs.suf, rs.sum + ls.suf);
x.mintime = std::min(ls.mintime, rs.mintime);
cmin(x.mintime, x.min + std::min(calc(x.pre, ls.sum + rs.pre), calc(x.suf, rs.sum + ls.suf)));
cmin(x.mintime, x.min + std::min(std::max(calc(x.len, ls.len), calc(x.len, rs.len)), calc(x.len, ls.suf + rs.pre)));
}
void pushdown(int x, int l, int r) {
int mid = (l + r) >> 1;
dfs(lson, l, mid, t[x].max);
dfs(rson, mid + 1, r, t[x].max);
t[x].max = -inf;
}
void dfs(int x, int l, int r, int v) {
if (t[x].min >= v) return;
if (t[x].minimin > v && t[x].mintime > v) {
int del = v - t[x].min;
t[x].sum.add(del); t[x].len.add(del);
t[x].pre.add(del); t[x].suf.add(del);
t[x].min = v; t[x].max = v;
return void();
}
t[x].max = v;
pushdown(x, l, r);
pushup(t[x], t[lson], t[rson]);
}
void build(int x, int l, int r) {
t[x].clear();
if (l == r) return t[x].set(a[l]);
int mid = (l + r) >> 1;
build(lson, l, mid); build(rson, mid + 1, r);
pushup(t[x], t[lson], t[rson]);
}
void update(int x, int l, int r, int ql, int qr, int v) {
if (ql <= l && r <= qr) return dfs(x, l, r, v);
int mid = (l + r) >> 1; pushdown(x, l, r);
if (ql <= mid) update(lson, l, mid, ql, qr, v);
if (qr > mid) update(rson, mid + 1, r, ql, qr, v);
pushup(t[x], t[lson], t[rson]);
}
void query(int x, int l, int r, int ql, int qr, TreeNode &ans) {
if (ql <= l && r <= qr) return pushup(ans, ans, t[x]);
int mid = (l + r) >> 1; pushdown(x, l, r);
if (ql <= mid) query(lson, l, mid, ql, qr, ans);
if (qr > mid) query(rson, mid + 1, r, ql, qr, ans);
}
} tr;
signed main() {
read(n, q);
for (int i = 1; i <= n; ++i) read(a[i]);
tr.build(1, 1, n);
while (q--) {
static int op, l, r, x;
read(op, l, r);
if (op == 0) read(x), tr.update(1, 1, n, l, r, x);
if (op == 1) {
SegmentTree::TreeNode ans = SegmentTree::TreeNode();
ans.clear();
tr.query(1, 1, n, l, r, ans);
printf("%lld\n", ans.len.val);
}
}
return 0;
}
标签:int,max,void,mid,P6792,Delta,区间,ql,SNOI2020
From: https://www.cnblogs.com/Maraschino/p/17022588.html