例题
CPU监控
要你维护对序列上的操作:
1、区间加
2、区间赋值
3、区间最大值
4、区间历史最值
使用线段树+标记维护,记录节点上发生的所有事件。
注意到一个线段树节点,如果进行了modify操作,那么接下来的加法都可以认为是modify。
那么一个节点上的标记长度就至多为2了。
设 ( ext{add}) 标记时节点实际要加的值,( ext{mod}) 表示覆盖。
考虑记录 ( ext{Add}) 标记为所有祖先的 ( ext{add}) 标记,历史上能达到的最大值。( ext{Mod}) 同理。
下放标记时注意到子节点上的标记发生时间在该点之前,依此时间顺序进行合并如下:
当下放加法标记,一方面由于 ( ext{Add}) 是上方加法操作中最大增加的值,用它结合实际值来更新历史最值。
若子节点无 ( ext{mod}) 标记那么标记向 ( ext{add}) 上打,否则向 ( ext{mod}) 上打。
并且要结合该子节点当前的操作值,更新对应操作的历史最值。
当下放覆盖标记,先用 ( ext{Mod}) (历史最大覆盖值)更新子节点的历史最值和 ( ext{Mod})。
然后用实际覆盖值修改实际最大值和实际覆盖值。
下放标记要注意顺序,加法在先,赋值在后。由于该写法下放标记的操作数量很小,可能存在速度优势。
下面是这个题的代码,它在2020年1月19日是洛谷的Rank1.
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N = 100005;
const int inf = 1e9;
struct IO_tp
{
static const int Sbuf=1<<21;
char buf[Sbuf], *S, *T, c; int f;
#define gc() (S==T?(T=(S=buf)+fread(buf,1,sizeof(buf),stdin),(S==T?EOF:*S++)):*S++)
template<class I>
inline IO_tp& operator >> (I &x)
{
for(f=1, c=gc(); c<'0'||c>'9'; c=gc()) if(c=='-') f=-1;
for(x=0; c>='0'&&c<='9'; c=gc()) x=x*10+(c^48); x*=f;
return *this;
}
inline char Readc()
{
for(c=gc(); c<'A'||c>'Z'; c=gc());
return c;
}
}io;
inline void ckmax(int&x,const int y)
{ x = x < y ? y : x; }
struct Segtree
{
struct node {
int max, add, mod;
int Max, Add, Mod;
node(): mod(-inf), Mod(-inf) {}
}t[N << 2];
#define lc (o << 1)
#define rc (o << 1 | 1)
void pushup(int o)
{
t[o].max = max(t[lc].max, t[rc].max);
t[o].Max = max(t[lc].Max, t[rc].Max);
}
void dadd(int o, int x, int y)
{
ckmax(t[o].Max, t[o].max + x);
t[o].max += y;
if(t[o].mod == -inf)
ckmax(t[o].Add, t[o].add + x), t[o].add += y;
else
ckmax(t[o].Mod, t[o].mod + x), t[o].mod += y;
}
void dmod(int o, int x, int y)
{
ckmax(t[o].Max, x);
ckmax(t[o].Mod, x);
t[o].mod = t[o].max = y;
}
void pushdown(int o)
{
if(t[o].add || t[o].Add)
{
dadd(lc, t[o].Add, t[o].add);
dadd(rc, t[o].Add, t[o].add);
t[o].Add = t[o].add = 0;
}
if(t[o].mod != -inf)
{
dmod(lc, t[o].Mod, t[o].mod);
dmod(rc, t[o].Mod, t[o].mod);
t[o].Mod = t[o].mod = -inf;
}
}
void build(int o, int l, int r)
{
if(l == r)
{
int k; io >> k;
t[o].max = t[o].Max = k;
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid); build(rc, mid + 1, r);
pushup(o);
}
void update(int o, int l, int r, int pl, int pr, int x)
{
if(l > pr || r < pl)
return;
if(l >= pl && r <= pr)
{
dadd(o, x, x);
return;
}
pushdown(o);
int mid = (l + r) >> 1;
update(lc, l, mid, pl, pr, x);
update(rc, mid + 1, r, pl, pr, x);
pushup(o);
}
void modify(int o, int l, int r, int pl, int pr, int x)
{
if(l > pr || r < pl)
return;
if(l >= pl && r <= pr)
{
dmod(o, x, x);
return;
}
pushdown(o);
int mid = (l + r) >> 1;
modify(lc, l, mid, pl, pr, x);
modify(rc, mid + 1, r, pl, pr, x);
pushup(o);
}
int query(int o, int l, int r, int pl, int pr)
{
if(l > pr || r < pl)
return -inf;
if(l >= pl && r <= pr)
return t[o].max;
pushdown(o);
int mid = (l + r) >> 1;
return max(query(lc, l, mid, pl, pr), query(rc, mid + 1, r, pl, pr));
}
int Query(int o, int l, int r, int pl, int pr)
{
if(l > pr || r < pl)
return -inf;
if(l >= pl && r <= pr)
return t[o].Max;
pushdown(o);
int mid = (l + r) >> 1;
return max(Query(lc, l, mid, pl, pr), Query(rc, mid + 1, r, pl, pr));
}
}z[1];
int main()
{
freopen("cpu.in", "r", stdin);
freopen("cpu.out", "w", stdout);
int n, q, l, r, x; char opt[3];
io >> n;
z->build(1, 1, n);
io >> q;
while(q--)
{
opt[0] = io.Readc();
io >> l >> r;
if(*opt == 'Q')
printf("%d
", z->query(1, 1, n, l, r));
if(*opt == 'A')
printf("%d
", z->Query(1, 1, n, l, r));
if(*opt == 'P')
io >> x, z->update(1, 1, n, l, r, x);
if(*opt == 'C')
io >> x, z->modify(1, 1, n, l, r, x);
}
return 0;
}
上面的做法有局限性。
设分段函数 (f(x)=max(x+a,b)),则区间修改操作均可以写成对一个区间作用上一个函数。
并且 (h(x)=max(f(x),g(x))) 也依然是这样的函数。
那么我们可以维护区间的分段函数 (f(x)) 和当前所有函数“最高轮廓” (g(x))。
考虑下放标记,(f(x)) 直接合并即可,考虑 (g(x));
相当于一堆函数作用到一个函数 (f') 上,最高的轮廓应当是这堆函数的 (g) 作用于 (f),然后再把这个和之前的 (g') 取较高的。
若要维护区间历史最值,下放标记时用 (g( ext{max})) 更新 ( ext{Max});再令 ( ext{max}=f( ext{max}))。
要注意你这个操作是对于这个区间整体而言的,就是说该区间的历史最值肯定要找最靠右的位置,所以要用当时的 ( ext{max}) 更新。
下面是清华集训那题的代码,只有单点查询(区间历史最值只要在 pushdown 里加两句话)
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 500004;
const ll inf = 1e16;
inline void ckmax(ll&x, ll y)
{ x = x < y ? y : x; }
struct IO_tp
{
static const int Sbuf=1<<21;
char buf[Sbuf], *S, *T, c; int f;
#define gc() (S==T?(T=(S=buf)+fread(buf,1,sizeof(buf),stdin),(S==T?EOF:*S++)):*S++)
template<class I>
inline IO_tp& operator >> (I &x)
{
for(f=1, c=gc(); c<'0'||c>'9'; c=gc()) if(c=='-') f=-1;
for(x=0; c>='0'&&c<='9'; c=gc()) x=x*10+(c^48); x*=f;
return *this;
}
inline char Readc()
{
for(c=gc(); c<'A'||c>'Z'; c=gc());
return c;
}
}io;
int n, m, a[N];
struct node {
ll a, b;
node(ll p = 0, ll q = -inf): a(p), b(q) {}
node operator + (const node& t)
{ return node(max(a + t.a, -inf), max(b + t.a, t.b)); }
ll operator () (ll x)
{ return max(x + a, b); }
void operator |= (const node& t)
{
ckmax(a, t.a);
ckmax(b, t.b);
}
} null;
struct Segtree
{
#define lc (o << 1)
#define rc (o << 1 | 1)
node f[N << 2], g[N << 2];
void Z(int o, const node& p, const node& q)
{
f[o] |= g[o] + p;
g[o] = g[o] + q;
}
void pushdown(int o)
{
Z(lc, f[o], g[o]); Z(rc, f[o], g[o]);
f[o] = g[o] = null;
}
void modify(int o, int l, int r, int pl, int pr, const node& z)
{
if(l > pr || r < pl)
return;
if(l >= pl && r <= pr)
{
Z(o, z, z);
return;
}
pushdown(o);
int mid = (l + r) >> 1;
modify(lc, l, mid, pl, pr, z);
modify(rc, mid + 1, r, pl, pr, z);
}
ll query(int o, int l, int r, int p)
{
if(l == r)
return g[o](a[l]);
pushdown(o);
int mid = (l + r) >> 1;
return mid >= p ? query(lc, l, mid, p) : query(rc, mid + 1, r, p);
}
ll Query(int o, int l, int r, int p)
{
if(l == r)
return f[o](a[l]);
pushdown(o);
int mid = (l + r) >> 1;
return mid >= p ? Query(lc, l, mid, p) : Query(rc, mid + 1, r, p);
}
}z[1];
int main()
{
io >> n >> m;
for(int i = 1; i <= n; ++i)
io >> a[i];
while(m--)
{
int opt, l, r, x;
io >> opt;
if(opt <= 3)
{
io >> l >> r >> x;
if(opt == 1)
z->modify(1, 1, n, l, r, node(x, 0));
else if(opt == 2)
z->modify(1, 1, n, l, r, node(-x, 0));
else
z->modify(1, 1, n, l, r, node(-inf, x));
}
else
{
io >> x;
if(opt == 4)
printf("%lld
", z->query(1, 1, n, x));
else
printf("%lld
", z->Query(1, 1, n, x));
}
}
return 0;
}