0. 前言
所谓线段树 Beats,就是吉老师打出来的线段树。
1. 基本操作
P6242 线段树 3
- 区间加
- 区间取 min
- 区间最大值
- 区间和
- 区间历史最大值
首先考虑 1 3 4 咋做。就是直接做对吧。
然后 5 咋做,我们发现难处在于历史最大值的维护。(废话)
比如我现在 \(+= k\),再 \(-= v\)。(都是正数)如果这两个操作执行完下传, 我们发现第一个操作执行完的历史最大值。因为标记把他们合并成了 \(k - v\)。
我们见招拆招,维护一个历史最大标记怎么样。就是 pushdown,我们先修改这个标记,再处理普通标记。
记这两个标记为 \(add_1, add_3\)。(为了适应后面的定义)
那么 \(add_1\) 为普通区间加标记。\(add_3\) 为下放 \(add_1\) 之前,\(add_1\) 的出现过的最大值。(标记的历史最大)
那这样我们是不是赢了。
然后 2. 取 min 其实很简单。相信大家都知道这个东西需要均摊分析。先说明处理过程。
维护最大值 \(maxa\),严格次大值 \(se\),最大值出现次数 \(cnt\)。递归时,假设取 min 的是 \(val\)。
- 如果 \(val > maxa\),那么结束。
- 如果 \(maxa > val > se\),那么可以通过 \(cnt\) 计算贡献。
- 否则暴力递归。
复杂度为什么是对的?设一个节点的容(我的理解:一个抽象的东西,描述节点的信息量多少?)为区间中数的种类数。由于暴力递归必然使得 最大值 和 严格次大值 合并。容 -= 1。而所有节点的种类数之和是 \(O(n\log n)\)(n 个数两两不同,每个数对 \(log\) 个节点贡献 1)
那这样我们就赢麻了。
怎么结合这两个玩意呢?我们考虑维护两套标记。
\(add_1, add_3\) 和 \(add_2, add_4\) 前者针对最大值,后者针对非最大值。
然后 pushdown 也需要修改。如果左边最大值 = 区间最大值,那么左边最大值继承区间最大值的标记,否则继承非最大值的标记。而左边非最大值继承区间非最大值标记。右边同理。
那这样我们就啥都会了。
#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
typedef long long ll;
typedef double db;
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define DE {cerr << "QAQ" << endl;}
#define dd(x) cerr << #x" = " << x << endl;
inline int read() {
int x=0, v=1,ch=getchar();
while('0'>ch||ch>'9') {
if(ch=='-') v=0;
ch=getchar();
}while('0'<=ch&&ch<='9') {
x=(x*10)+(ch^'0');
ch=getchar();
}return v?x:-x;
}
const int MAX = 5e5 + 5;
const int INF = -2e9;
struct node {
ll sum;
int maxa, se, maxb, cnt, len;
int add1, add2, add3, add4;
void upd(int k1, int k2, int k3, int k4) {
sum += 1ll * cnt * k1 + 1ll * (len - cnt) * k2;
maxb = max(maxb, maxa + k3); // ÕâÀï k3 ÊÇ ×î´óÖµ + ÀúÊ·×î´ó±ê¼Ç¡£
maxa += k1;
if(se != INF) se += k2;
add3 = max(add3, add1 + k3);
add4 = max(add4, add2 + k4);
add1 += k1, add2 += k2;
}
void upd2(int v) {
sum += len * v;
maxb = max(maxb, maxa + v);
maxa += v;
if(se != INF) se += v;
add3 = max(add3, add1 + v);
add4 = max(add4, add2 + v);
add1 += v;
add2 += v;
}
} tr[MAX << 2];
#define ls x << 1, l, mid
#define rs x << 1 | 1, mid + 1, r
#define rt 1, 1, n
void ps(int x) {
node &m = tr[x];
node l = tr[x << 1], r = tr[x << 1 | 1];
m.sum = l.sum + r.sum;
m.maxa = max(l.maxa, r.maxa);
m.maxb = max(l.maxb, r.maxb);
if(l.maxa == r.maxa) {
m.se = max(l.se, r.se);
m.cnt = l.cnt + r.cnt;
}else if(m.maxa == l.maxa) {
m.se = max(l.se, r.maxa);
m.cnt = l.cnt;
}else if(m.maxa == r.maxa){
m.se = max(r.se, l.maxa);
m.cnt = r.cnt;
}
}
void pd(int x) {
int v = max(tr[x << 1].maxa, tr[x << 1 | 1].maxa);
if(v == tr[x << 1].maxa)
tr[x << 1].upd(tr[x].add1, tr[x].add2, tr[x].add3, tr[x].add4);
else tr[x << 1].upd(tr[x].add2, tr[x].add2, tr[x].add4, tr[x].add4);
if(v == tr[x << 1 | 1].maxa)
tr[x << 1 | 1].upd(tr[x].add1, tr[x].add2, tr[x].add3, tr[x].add4);
else tr[x << 1 | 1].upd(tr[x].add2, tr[x].add2, tr[x].add4, tr[x].add4);
tr[x].add1 = tr[x].add2 = tr[x].add3 = tr[x].add4 = 0;
}
void build(int x, int l, int r) {
tr[x].len = r - l + 1;
if(l == r) {
int v = read();
tr[x].sum = tr[x].maxa = tr[x].maxb = v;
tr[x].se = INF;
tr[x].cnt = 1;
return ;
}
int mid = l + r >> 1;
build(ls), build(rs), ps(x);
}
void mdf_min(int x, int l, int r, int s, int t, int v){
if(v > tr[x].maxa) return ;
if(s <= l && r <= t && v > tr[x].se) {
int d = tr[x].maxa - v;
tr[x].sum -= 1ll * d * tr[x].cnt;
tr[x].add1 -= d;
tr[x].maxa = v;
return ;
}
int mid = l + r >> 1; pd(x);
if(s <= mid) mdf_min(ls, s, t, v);
if(mid < t) mdf_min(rs, s, t, v);
ps(x);
}
void mdf_add(int x, int l, int r, int s, int t, int v) {
if(s <= l && r <= t) {
tr[x].upd2(v);
return ;
}
int mid = l + r >> 1; pd(x);
if(s <= mid) mdf_add(ls, s, t, v);
if(mid < t) mdf_add(rs, s, t, v);
ps(x);
}
ll ask_sum(int x, int l, int r, int s, int t) {
if(s <= l && r <= t) return tr[x].sum;
ll ret = 0; int mid = l + r >> 1; pd(x);
if(s <= mid) ret += ask_sum(ls, s, t);
if(mid < t) ret += ask_sum(rs, s, t);
return ret;
}
int ask_maxa(int x, int l, int r, int s, int t) {
if(s <= l && r <= t) return tr[x].maxa;
int mid = l + r >> 1; int ret = INF; pd(x);
if(s <= mid) ret = max(ret, ask_maxa(ls, s, t));
if(mid < t) ret = max(ret, ask_maxa(rs, s, t));
return ret;
}
int ask_maxb(int x, int l, int r, int s, int t) {
if(s <= l && r <= t) return tr[x].maxb;
int mid = l + r >> 1; int ret = INF; pd(x);
if(s <= mid) ret = max(ret, ask_maxb(ls, s, t));
if(mid < t) ret = max(ret, ask_maxb(rs, s, t));
return ret;
}
int n, m;
signed main() {
// fin;
n = read(), m = read();
build(1, 1, n);
for(int i = 1; i <= m; ++ i) {
int op = read(), l = read(), r = read();
if(op == 1) {
int k = read();
mdf_add(rt, l, r, k);
}else if(op == 2) {
int k = read();
mdf_min(rt, l, r, k);
}
else if(op == 3) printf("%lld\n", ask_sum(rt, l, r));
else if(op == 4) printf("%d\n", ask_maxa(rt, l, r));
else printf("%d\n", ask_maxb(rt, l, r));
}
return 0;
}
CPU 监控
区间加,区间赋值,区间最大值,区间历史最大值。
多了个区间赋值。意味着我们需要理清楚两个标记之间的关系!(就像线段树 2)
实际上我们只有一个标记,\((add, cov)\),表示先区间加 \(add\),再区间赋值成 \(cov\)。
因为如果我们进行了一次区间赋值,我们的区间加操作都可以变成区间赋值操作。
我们维护标记 \(add, hadd, cov, hcov, flag\) 表示:
区间加标记,区间加标记的历史最大,区间赋值标记,区间赋值标记的历史最大。
点击查看代码
#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
typedef long long ll;
typedef double db;
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define DE {cerr << "QAQ" << endl;}
#define dd(x) cerr << #x" = " << x << endl;
inline int read() {
int x=0, v=1,ch=getchar();
while('0'>ch||ch>'9') {
if(ch=='-') v=0;
ch=getchar();
}while('0'<=ch&&ch<='9') {
x=(x*10)+(ch^'0');
ch=getchar();
}return v?x:-x;
}
inline char readc() {
char ch = 0;
while(ch < 'A' || ch > 'Z') ch = getchar();
return ch;
}
int n;
const int MAX = 1e5 + 5;
struct node {
int maxa, maxb;
int add, hadd; // ¼Ó·¨±ê¼Ç£¬¼Ó·¨±ê¼ÇµÄÀúÊ·×î´ó
int cov, hcov; // ¸³Öµ±ê¼Ç£¬¸³Öµ±ê¼ÇµÄÀúÊ·×î´ó
int fl; // ÊÇ·ñ½øÐÐÁ˸³Öµ±ê¼Ç
void upd_add(int k1, int k2) {
// ËùνµÄ¸³Öµ ºÍ ¼Ó·¨»¥Ïàת»¯¡£
// ¸Ð¾õÉÏ£º½øÐÐ cover Ç°£¬¹±Ï׶¼ÔÚ¸³Öµ²Ù×÷ͳ¼Æ¡£
maxb = max(maxb, maxa + k2);
maxa += k1;
if(fl) {
hcov = max(hcov, cov + k2);
cov += k1;
}else {
hadd = max(hadd, add + k2);
add += k1;
}
}
void upd_cov(int k1, int k2) {
fl = 1;
maxb = max(maxb, k2);
maxa = cov = k1;
hcov = max(hcov, k2);
}
} tr[MAX << 2];
#define ls x << 1, l, mid
#define rs x << 1 | 1, mid + 1, r
#define rt 1, 1, n
void ps(int x) {
tr[x].maxa = max(tr[x << 1].maxa, tr[x << 1 | 1].maxa);
tr[x].maxb = max(tr[x << 1].maxb, tr[x << 1 | 1].maxb);
}
void pd(int x) {
if(tr[x].add || tr[x].hadd) {
tr[x << 1].upd_add(tr[x].add, tr[x].hadd);
tr[x << 1 | 1].upd_add(tr[x].add, tr[x].hadd);
tr[x].add = tr[x].hadd = 0;
}
if(tr[x].fl) {
tr[x << 1].upd_cov(tr[x].cov, tr[x].hcov);
tr[x << 1 | 1].upd_cov(tr[x].cov, tr[x].hcov);
tr[x].fl = 0;
}
}
void add(int x, int l, int r, int s, int t, int v) {
if(s <= l && r <= t) { tr[x].upd_add(v, max(v, 0)); return ; }
int mid = l + r >> 1; pd(x);
if(s <= mid) add(ls, s, t, v);
if(mid < t) add(rs, s, t, v);
ps(x);
}
void mdf(int x, int l, int r, int s, int t, int v) {
if(s <= l && r <= t) { tr[x].upd_cov(v, v); return ; }
int mid = l + r >> 1; pd(x);
if(s <= mid) mdf(ls, s, t, v);
if(mid < t) mdf(rs, s, t, v);
ps(x);
}
int hans, ans;
void qry(int x, int l, int r, int s, int t) {
if(s <= l && r <= t) {
ans = max(ans, tr[x].maxa);
hans = max(hans, tr[x].maxb);
return ;
} int mid = l + r >> 1; pd(x);
if(s <= mid) qry(ls, s, t);
if(mid < t) qry(rs, s, t);
ps(x);
}
void build(int x, int l, int r) {
if(l == r) {
int v = read();
tr[x].maxa = tr[x].maxb = v;
return ;
}
int mid = l + r >> 1;
build(ls), build(rs), ps(x);
}
signed main() {
// fin;
n = read();
build(rt);
int T = read();
while(T--) {
char op = readc();
int l = read(), r = read();
if(op == 'Q') {
ans = - 1 << 31;
qry(rt, l, r);
printf("%d\n", ans);
}else if(op == 'A'){
hans = - 1 << 31;
qry(rt, l, r);
printf("%d\n", hans);
}else if(op == 'P') {
int c = read();
add(rt, l, r, c);
}else {
int c = read();
mdf(rt, l, r, c);
}
}
return 0;
}