单侧递归线段树
\(\text{luogu-4198}\)
单侧递归线段树。
之前写过这个东西,但已经完全忘记了,训练遇到了这种题就顺便总结一下。
这题算得上是单侧递归线段树的模板题了,简要题意就是找作为前缀最大的楼房斜率的个数。
这个东西比较抽象,因为没办法容易的左右区间合并。令 \(t_p\) 表示 \([l_p,r_p]\) 这个区间的答案,会发现如果只维护这个信息,我们无法做到 pushup。问题在于,右区间可能有比左区间斜率小的,也就是说我们只关注每个区间的斜率最大值,于是我们再记录一个 \(mx_p\) 表示区间斜率最大值。
这样我们可以类似分治的找要合并的右区间中斜率大于 \(mx_{ls}\) 的个数。
这就是单侧递归线段树,时间复杂度是 \(O(n \log^2 n)\) 的。下面是核心代码:
struct Seg {
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid (l + r >> 1)
ll t[N << 2]; double mx[N << 2];
ll qry(ll p, ll l, ll r, double x) {
if(mx[p] <= x) return 0;
if(l == r) return (mx[p] > x);
if(mx[ls] <= x) return qry(rs, mid + 1, r, x);
return t[p] - t[ls] + qry(ls, l, mid, x);
}
void pushup(ll p, ll l, ll r) {
mx[p] = max(mx[ls], mx[rs]);
t[p] = t[ls] + qry(rs, mid + 1, r, mx[ls]);
}
void upd(ll p, ll l, ll r, ll x, ll y) {
if(l == r) { t[p] = 1, mx[p] = 1.0 * y / x; return; }
if(x <= mid) upd(ls, l, mid, x, y);
else upd(rs, mid + 1, r, x, y);
pushup(p, l, r);
}
#undef ls
#undef rs
#undef mid
} T;
\(\text{luogu-9130}\)
今天训练的题。
这个题和 luogu-4198 的相似点在于,左区间对右区间也有影响,不能单纯合并。
考虑单侧递归线段树,首先肯定要维护答案 \(w_p\),其次影响右区间的就是溢出的干草个数 \(ot_p\)。
为了能正常更新 \(ot_p\),还需要维护当前区间占用了多少个位置 \(c_p\)。
\(c_p\) 最容易更新,即 \(c_p = c_{ls_p} + \min(ot_{ls_p} + c_{rs_p}, r - mid)\),左区间占用个数加上右区间占用个数。
其次 \(ot_p = ot_{rs_p} + \max(ot_{ls_p} + c_{rs_p} - (r - mid), 0)\),右区间溢出个数加上左区间可能溢出的个数。
下面就是答案的更新,\(w_p = w_{ls} + f(rs, ot_{ls})\),其中,\(f(x,y)\) 函数表示 \([l_x,r_x]\) 这个区间在接受左区间的溢出之后的答案,具体的,也是类似分治的实现:
- 若没有溢出到当前区间的右半区间,则答案为 \(f(ls_x,y)+w_x-w_{ls_x}\)。
- 否则,答案为 \(\frac{(l+r) \cdot (r-l+1)}{2} + f(rs_x,y-(mid-l+1-c_{ls_x})+ot_{ls_x})\)。
和 luogu-4198 的核心思路几乎相同。
struct Seg {
#define mid (l + r >> 1)
ll cnt, ls[N], rs[N], c[N], w[N], ot[N];
ll S(ll l, ll r) { return (l + r) % MOD * ((r - l + 1) % MOD) % MOD * inv2 % MOD; }
ll f(ll p, ll l, ll r, ll x) {
if(!x) return w[p];
if(l == r) return l % MOD;
if(x + c[ls[p]] <= mid - l + 1) return (f(ls[p], l, mid, x) + w[p] - w[ls[p]] + MOD) % MOD;
return (S(l, mid) + f(rs[p], mid + 1, r, x - (mid - l + 1 - c[ls[p]]) + ot[ls[p]])) % MOD;
}
void pushup(ll p, ll l, ll r) {
c[p] = c[ls[p]] + min(ot[ls[p]] + c[rs[p]], r - mid);
w[p] = (w[ls[p]] + f(rs[p], mid + 1, r, ot[ls[p]])) % MOD;
ot[p] = ot[rs[p]] + max(ot[ls[p]] + c[rs[p]] - r + mid, 0ll);
}
void upd(ll &p, ll l, ll r, ll x, ll y) {
if(!p) p = (++ cnt);
if(l == r) {
if(!y) c[p] = w[p] = ot[p] = 0;
else c[p] = 1, w[p] = x % MOD, ot[p] = y - 1;
return;
}
if(x <= mid) upd(ls[p], l, mid, x, y);
else upd(rs[p], mid + 1, r, x, y);
pushup(p, l, r);
}
#undef mid
} T;
本文来自博客园,作者:So_noSlack,转载请注明原文链接:https://www.cnblogs.com/So-noSlack/p/20033811

浙公网安备 33010602011771号