特定条件下线段树暴力单点修改的小优化
有的时候一些区间修改条件不符合区间的性质(比如加法在区间上可以以和为区间性质,但是众数不行)。但是某些特定的条件下可以借助其他的具有区间的性质的条件来优化暴力单点修改。
CF438D The Child and Sequence
题意:实现区间取模、区间求和,单点修改。
区间求和和单点修改很简单,但是问题在于区间取模。区间取模是一个不具有区间性质的条件。
显然,对一堆数取模再累加的结果和对其和取模的结果一定是不同的。像这种操作显然只能暴力修改(当然也可以用优雅的暴力,也就是分块做,或者其他方式,这里说的是线段树)。当然,暴力单点修改的结果一定是喜得TLE。
虽然区间取模不满足能够直接对区间进行操作,但是取模操作却有一个很明显的特征:只有对大于模数的数才需要进行取模。如果一段区间内的数字都小于这个模数,显然是不用再去暴力修改的。如果能有一个标记,表示这个区间存在着一个大于模数的数,那么意味着这个区间需要被修改。
这个特别的标记恰好和维护区间最大值相对应了。如果这个区间的最大值大于模数,这段区间才需要进行操作,否则不需要。同理,大区间再递归分成小区间时,再判断。这样一来,原本十分暴力的单点修改就变成了十分优雅的暴力单点修改了。
大致代码如下。
#inlcude <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Node { //线段树结点定义
ll sum, maxn;
int lc, rc;
};
const int MAXN = 1e5 + 10;
Node tr[MAXN << 4];
int tot = 1; // 堆式存储也可以
ll a[MAXN];
// 看个人怎么喜欢命名吧,有的人喜欢用pushup
// 主要是自己打update的速度比pushup更快
// 而且对我来说这个是对当前结点的更新
inline void update(int now) {
tr[now].sum = tr[tr[now].lc].sum + tr[tr[now].rc].sum;
tr[now].maxn = max(tr[tr[now].lc].maxn. tr[tr[now].rc].maxn);
}
void build(int l, int r, int now) {
if (l == r) {
tr[now].sum = tr[now].maxn = a[l];
return;
}
int mid = l + ((r - l) >> 1);
tr[now].lc = ++ tot;
build(l, mid, tr[now].lc);
tr[now].rc = ++ tot;
build(mid + 1, r, tr[now].rc);
update(now);
}
ll query(int sl, int sr, int ll, int rr, int now) {
if (sl <= ll && rr <= sr)
return tr[now].sum;
ll res = 0;
int mid = ll + ((rr - ll) >> 1);
if (sl <= mid)
res += query(sl, sr, ll, mid, tr[now].lc);
if (sr > mid)
res += query(sl, sr, mid + 1, rr, tr[now].rc);
return res;
}
void change(int x, ll val, int ll, int rr, int now) {
if (ll == rr) {
tr[now].sum = tr[now].maxn = val;
return;
}
int mid = ll + ((rr - ll) >> 1);
if (x <= mid)
change(x, val, ll, mid, tr[now].lc);
else
change(x, val, mid + 1, rr, tr[now].rc);
update(now);
}
// 有点像是单点修改与区间修改的混合,当然这种写法不一定(也应该)不是最好的
void mod(int sl, int sr, ll p, int ll, int rr, int now) {
if (ll == rr) {
if (tr[now].sum >= p) {
tr[now].maxn = (tr[now].sum %= p);
}
return;
}
if (sl <= ll && rr <= sr) {
if (tr[now].maxn >= p) {
int mid = ll + ((rr - ll) >> 1);
if (tr[tr[now].lc].maxn >= p)
mod(sl, sr, p, ll, mid, tr[now].lc);
if (tr[tr[now].rc].maxn >= p)
mod(sl, sr, p, mid + 1, rr, tr[now].rc);
update(now);
}
return;
}
int mid = ll + ((rr - ll) >> 1);
if (sl <= mid)
mod(sl, sr, p, ll, mid, tr[now].lc);
if (sr > mid)
mod(sl, sr, p, mid + 1, rr, tr[now].rc);
update(now);
}