线段树分治 笔记
本质上,这只是一种结合了线段树和 CDQ 分治的思想的东西,和线段树并没有太大的关联。
首先,如果不考虑时间的问题,也就是说加入了所有商品后,再进行询问。那么按商店从小到大加到对应的 01 Trie 里,可持久化即可解决问题。
接下来考虑时间问题,先把每个询问的时间区间都打到一个时间轴上,将这个时间轴同时看作线段树维护的区间,处理一个询问,就按照线段树的方式把它分割成若干个询问。
如时间轴总长度为 \(5\),询问 \([2,5]\to\) 询问 \([2,2],[3,3],[4,5]\)。
把询问挂到线段树上后,考虑每个线段树区间包含哪些修改。
某个时间区间包含的修改一定是影响这个区间里所有的询问的,因为修改是区间内一点,而询问覆盖了整个区间。
将这个时间区间的修改打到可持久化 Trie 上,询问即可。
然后按照线段树的定义,把修改拆成左右两半,继续递归。
特殊商品的加入时间可以看作 \(0\),但并不用把特殊商品放到线段树上,只需要在最初还没把询问挂到线段树的时候,单独处理每个询问对于特殊商品的答案即可。
struct Q0 {
int s, v, t;
bool operator<(const Q0 &x) const {
return s < x.s;
}
} q0[N], q0l[N], q0r[N];
struct Q1 {
int l, r, tl, tr, x;
} q1[N];
namespace TRIE { ... } // 可持久化 Trie
using namespace TRIE;
namespace Segtree {
#define ls rt << 1
#define rs rt << 1 | 1
vector<int> tr[N];
int st[N], top;
void update(int rt, int l, int r, int x, int y, int v) {
if (l > r or l > y or r < x)
return;
if (x <= l and r <= y) {
tr[rt].push_back(v); // 把询问按线段树的区间拆分
return;
}
int mid = l + r >> 1;
update(ls, l, mid, x, y, v);
update(rs, mid + 1, r, x, y, v);
}
void calc(int rt, int l0, int r0) {
idx = top = 0;
for (int i = l0; i <= r0; i++) {
st[++top] = q0[i].s;
root[top] = ++idx;
insert(root[top], root[top - 1], q0[i].v);
}
for (int i : tr[rt]) {
// 下面这两个,是先 upper_bound,再减去 1,等价于找第一个小于等于 x 的数,这样子才能真正包含询问的商店区间
int l = upper_bound(st + 1, st + top + 1, q1[i].l - 1) - st - 1;
int r = upper_bound(st + 1, st + top + 1, q1[i].r) - st - 1;
ans[i] = max(ans[i], query(root[l], root[r], q1[i].x));
}
}
void solve(int rt, int tl, int tr, int l0, int r0) {
if (l0 > r0)
return;
calc(rt, l0, r0);
if (tl == tr)
return;
int cl = 0, cr = 0;
int tmid = tl + tr >> 1;
for (int i = l0; i <= r0; i++) // 按照时间把修改分成左右两半
if (q0[i].t <= tmid)
q0l[++cl] = q0[i];
else
q0r[++cr] = q0[i];
for (int i = l0, j = 1; j <= cl; i++, j++)
q0[i] = q0l[j];
for (int i = l0 + cl, j = 1; j <= cr; i++, j++)
q0[i] = q0r[j];
solve(ls, tl, tmid, l0, l0 + cl - 1);
solve(rs, tmid + 1, tr, l0 + cl, r0);
}
}
using namespace Segtree;
signed main() {
IOS;
cin >> n >> m;
for (int i = 1, x; i <= n; i++) {
cin >> x;
root[i] = ++idx;
insert(root[i], root[i - 1], x);
}
for (int i = 1, opt, l, r, x, d; i <= m; i++) {
cin >> opt;
if (!opt) {
cin >> x >> d;
q0[++n0] = {x, d, n0};
} else {
cin >> l >> r >> x >> d;
q1[++n1] = {l, r, max(1ll, n0 - d + 1), n0, x};
ans[n1] = query(root[l - 1], root[r], x); // 提前处理特殊商品
}
}
for (int i = 1; i <= n1; i++)
update(1, 1, n0, q1[i].tl, q1[i].tr, i);
sort(q0 + 1, q0 + n0 + 1);
solve(1, 1, n0, 1, n0);
for (int i = 1; i <= n1; i++)
cout << ans[i] << "\n";
return 0;
}

浙公网安备 33010602011771号