【学习笔记】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;
}
posted @ 2024-11-16 14:33  liukejie  阅读(1)  评论(0编辑  收藏  举报