【学习笔记】Segment Tree Beats/吉司机线段树
链接
区间最值操作
HDU-5306
支持对区间取 \(\min\),维护区间 \(\max\),查询区间和。
很容易想到一个暴力,我们每一次找出这个区间的最大值 \(mx\),如果 \(mx>x\),那么暴力修改这个位置的值,否则已经修改完毕,退出,时间复杂度为 \(O(n^2 \log n)\)。
打一打补丁,对线段树上的每一个区间维护区间最大值 \(mx\),这个区间中最大值出现的次数 \(t\),区间次大值 \(se\),当然还要维护区间和 \(sum\)。
现在考虑打上区间取 \(\min\) 标记
- 如果 \(mx\le x\),那么对 \(sum\) 就没有修改。
- 如果 \(se<x<mx\),那么 \(sum=sum-(mx-x)\times t\)。
- 如果 \(x\le se<mx\),此时无法直接更新节点信息,故向下左右子树递归。我们分别 DFS 这个节点的两个孩子,如果当前 DFS 的过程中遇到了前两种情况,就直接修改打上标记然后退出,否则就继续 DFS。
点击查看代码
#include <bits/stdc++.h>
//#define int long long
#define rep(i, l, r) for(int i = l; i <= r; ++ i)
#define per(i, r, l) for(int i = r; i >= l; -- i)
using namespace std;
char gc()
{
static char buf[1 << 20], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1 ++;
}
void read(int &n)
{
char c = gc(), w = 0;
for(; c < '0' || c > '9'; c = gc()) w = c == '-';
for(n = 0; c >= '0' && c <= '9'; c = gc()) n = (n << 1) + (n << 3) + c - 48;
n = w ? -n : n;
}
const int N = 1e6 + 10;
#define ls rt + rt
#define rs rt + rt + 1
int mx[N << 2], se[N << 2], cnt[N << 2], tag[N << 2];
long long sum[N << 2];
int p[N], n, m;
void update(int rt)
{
sum[rt] = sum[ls] + sum[rs];
if(mx[ls] == mx[rs])
{
mx[rt] = mx[ls];
cnt[rt] = cnt[ls] + cnt[rs];
se[rt] = max(se[ls], se[rs]);
}
else if(mx[ls] < mx[rs])
{
mx[rt] = mx[rs];
cnt[rt] = cnt[rs];
se[rt] = max(mx[ls], se[rs]);
}
else
{
mx[rt] = mx[ls];
cnt[rt] = cnt[ls];
se[rt] = max(se[ls], mx[rs]);
}
}
void build(int rt, int l, int r)
{
tag[rt] = 0;
if(l == r)
{
mx[rt] = sum[rt] = p[l];
se[rt] = -1; cnt[rt] = 1;
return ;
}
int mid = (l + r) >> 1;
build(ls, l, mid); build(rs, mid + 1, r);
update(rt);
}
void push_up(int rt, int v)
{
if(mx[rt] <= v) return;
sum[rt] -= 1ll * (mx[rt] - v) * cnt[rt];
mx[rt] = tag[rt] = v;
}
void push_down(int rt)
{
if(!tag[rt]) return;
push_up(ls, tag[rt]); push_up(rs, tag[rt]);
tag[rt] = 0;
}
void change(int rt, int l, int r, int x, int y, int v)
{
if(mx[rt] <= v) return;
if(x <= l && r <= y && se[rt] <= v) {push_up(rt, v); return;}
int mid = (l + r) >> 1; push_down(rt);
if(mid >= x) change(ls, l, mid, x, y, v);
if(mid < y) change(rs, mid + 1, r, x, y, v);
update(rt);
}
int askmax(int rt, int l, int r, int x, int y)
{
if(x <= l && r <= y) return mx[rt];
push_down(rt);
int mid = (l + r) >> 1, ans = -1;
if(mid >= x) ans = max(ans, askmax(ls, l, mid, x, y));
if(mid < y) ans = max(ans, askmax(rs, mid + 1, r, x, y));
return ans;
}
long long asksum(int rt, int l, int r, int x, int y)
{
if(x <= l && r <= y) return sum[rt];
push_down(rt);
int mid = (l + r) >> 1; long long ans = 0;
if(mid >= x) ans += asksum(ls, l, mid, x, y);
if(mid < y) ans += asksum(rs, mid + 1, r, x, y);
return ans;
}
void solve()
{
read(n); read(m);
rep(i, 1, n) read(p[i]);
build(1, 1, n);
rep(i, 1, m)
{
int opt, x, y, t;
read(opt); read(x); read(y);
if(!opt) read(t), change(1, 1, n, x, y, t);
else if(opt == 1) printf("%d\n", askmax(1, 1, n, x, y));
else printf("%lld\n", asksum(1, 1, n, x, y));
}
}
main()
{
int T; read(T); for(; T; -- T) solve();
return 0;
}
历史最值操作
Tyvj 1518 CPU监控/bzoj3060/P4314 CPU监控
支持区间加,区间覆盖,查询区间 \(\max\) 与区间历史 \(\max\)。
一开始的想法是用当前最大值及当前的标记维护历史最大值,但可以举出反例:
8
1 1 10 1 1 1 1 1
3
P 1 8 100
P 1 8 -10
A 3 3
正确答案是 \(110\),但输出 \(100\),因为第二个操作把会减小第一个操作的 add 标记,儿子的历史 \(\max\) 得不到及时的更新。
所以记录以下信息:
mx: 区间最大值
hmx:区间历史最大值
vis:是否进行过赋值操作
sum:当前节点上次 push_down 之后的加和
asg: 当前节点上次 push_down 之后的赋值操作 (赋值之后的区间加操作算入赋值)
summx:上次 push_down 之后达到的最大加和
asgmx:上次 push_down 之后赋的最大值
点击查看代码
#include <bits/stdc++.h>
//#define int long long
#define rep(i, l, r) for(int i = l; i <= r; ++ i)
#define per(i, r, l) for(int i = r; i >= l; -- i)
using namespace std;
#define gc getchar
void read(int &n)
{
char c = gc(), w = 0;
for(; c < '0' || c > '9'; c = gc()) w = c == '-';
for(n = 0; c >= '0' && c <= '9'; c = gc()) n = (n << 1) + (n << 3) + c - 48;
n = w ? -n : n;
}
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int n, m;
int p[N];
#define ls rt + rt
#define rs rt + rt + 1
int mx[N << 2], hmx[N << 2], sum[N << 2], asg[N << 2], summx[N << 2], asgmx[N << 2];
bool vis[N << 2];
void update(int rt)
{
mx[rt] = max(mx[ls], mx[rs]);
hmx[rt] = max(hmx[ls], hmx[rs]);
}
void build(int rt, int l, int r)
{
if(l == r)
{
mx[rt] = hmx[rt] = p[l];
return;
}
int mid = (l + r) >> 1; build(ls, l, mid); build(rs, mid + 1, r);
update(rt);
}
void push_up_add(int rt, int val, int mxval)
{
if(vis[rt]) asgmx[rt] = max(asgmx[rt], asg[rt] + mxval), asg[rt] += val;
else summx[rt] = max(summx[rt], sum[rt] + mxval), sum[rt] += val;
hmx[rt] = max(hmx[rt], mx[rt] + mxval);
mx[rt] += val;
}
void push_up_change(int rt, int val, int mxval)
{
hmx[rt] = max(hmx[rt], mxval);
if(vis[rt]) asgmx[rt] = max(asgmx[rt], mxval);
else vis[rt] = 1, asgmx[rt] = mxval;
mx[rt] = asg[rt] = val;
}
void push_down(int rt)
{
push_up_add(ls, sum[rt], summx[rt]);
push_up_add(rs, sum[rt], summx[rt]);
sum[rt] = summx[rt] = 0;
if(!vis[rt]) return;
push_up_change(ls, asg[rt], asgmx[rt]);
push_up_change(rs, asg[rt], asgmx[rt]);
vis[rt] = asg[rt] = asgmx[rt] = 0;
}
void add(int rt, int l, int r, int x, int y, int val)
{
if(x <= l && r <= y) {push_up_add(rt, val, val); return;}
int mid = (l + r) >> 1; push_down(rt);
if(mid >= x) add(ls, l, mid, x, y, val);
if(mid < y) add(rs, mid + 1, r, x, y, val);
update(rt);
}
void change(int rt, int l, int r, int x, int y, int val)
{
if(x <= l && r <= y) {push_up_change(rt, val, val); return;}
int mid = (l + r) >> 1; push_down(rt);
if(mid >= x) change(ls, l, mid, x, y, val);
if(mid < y) change(rs, mid + 1, r, x, y, val);
update(rt);
}
int History(int rt, int l, int r, int x, int y)
{
if(x <= l && r <= y) return hmx[rt];
int mid = (l + r) >> 1, ans = -INF; push_down(rt);
if(mid >= x) ans = max(ans, History(ls, l, mid, x, y));
if(mid < y) ans = max(ans, History(rs, mid + 1, r, x, y));
return ans;
}
int Max(int rt, int l, int r, int x, int y)
{
if(x <= l && r <= y) return mx[rt];
int mid = (l + r) >> 1, ans = -INF; push_down(rt);
if(mid >= x) ans = max(ans, Max(ls, l, mid, x, y));
if(mid < y) ans = max(ans, Max(rs, mid + 1, r, x, y));
return ans;
}
main()
{
read(n);
rep(i, 1, n) read(p[i]);
build(1, 1, n); read(m);
rep(i, 1, m)
{
char ch; cin >> ch;
int x, y, t;
read(x); read(y);
if(ch == 'Q') printf("%d\n", Max(1, 1, n, x, y));
if(ch == 'A') printf("%d\n", History(1, 1, n, x, y));
if(ch == 'P') read(t), add(1, 1, n, x, y, t);
if(ch == 'C') read(t), change(1, 1, n, x, y, t);
}
return 0;
}