「PR #15」二叉搜索树
考虑询问算的是个什么东西:假设我们在一个点处按时间顺序插入的数的序列为 ,查询的值是 ,那么本质上来说,是要找 序列比 大的部分的前缀最大值之和,以及比 小的部分的前缀最小值之和(还要特判 )。以值域为下标维护这个数的插入时间。那么我们要算的是 区间的前缀最小值的下标之和,以及 的后缀最小值的下标之和。对每个节点开棵兔队线段树即可过掉菊花(修改数很少)的部分分。
考虑怎么推广到链加。考虑把操作离线下来,利用树上差分的思想,用线段树合并维护出每个节点的线段树长什么样(在端点插入,在 LCA 处删)。但是这样在查的时候要限制产生贡献的值必须在这个查询操作之前出现。仔细想一下,其实这个限制也可以直接对查询加深限制。具体地,我们令当前询问在时间轴上的位置是 ,那么直接在查询之前让“之前的区间最值”为 就可以了(一般来讲是极大/小值)。
分析合并兔队线段树的复杂度。每次 pushup
时必定合并掉了一个节点,所以复杂度就是 ,空间复杂度 。
然后就完了,感觉很简单,但是场上竟然没想到最后一步。并且这个题的通过情况也不是很好,挺抽象的。是不是大家都没想到兔队线段树可以合并?
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
using LL = long long;
using pii = pair<int, int>;
inline int read() {
int f = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) f = f * 10 + (c - '0'), c = getchar();
return f;
}
const int N = 200005, lim = 200000, inf = 1e9;
int n, m;
vector<int> G[N];
int fa[N][21], dep[N], op[N], w[N];
void dfs1(int p, int lst) {
fa[p][0] = lst, dep[p] = dep[lst] + 1;
for (int i = 1; i <= 20; i++) fa[p][i] = fa[fa[p][i - 1]][i - 1];
for (auto x : G[p]) {
if (x != lst) dfs1(x, p);
}
}
inline int lca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i = 20; i >= 0; i--) if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if (x == y) return x;
for (int i = 20; i >= 0; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
inline int jump(int x, int k) {
for (int i = 0; i <= 20; i++) if ((k >> i) & 1) x = fa[x][i];
return x;
}
vector<int> add[N], del[N];
vector<pii> qry[N];
LL ans[N];
struct Node {
int lc, rc, m;
LL f;
};
int rt[2][N], cnt;
Node tr[N * 80];
LL query(int p, int l, int r, int lm) {
if (l == r) return tr[p].m < lm ? tr[p].f : 0;
int mid = (l + r) >> 1;
if (tr[tr[p].lc].m >= lm) return query(tr[p].rc, mid + 1, r, lm);
return query(tr[p].lc, l, mid, lm) + tr[p].f - tr[tr[p].lc].f;
}
inline void pushup(int p, int l, int r) {
int mid = (l + r) >> 1;
tr[p].m = min(tr[tr[p].lc].m, tr[tr[p].rc].m);
tr[p].f = tr[tr[p].lc].f + query(tr[p].rc, mid + 1, r, tr[tr[p].lc].m);
}
void update(int &p, int l, int r, int x, int f, int w) {
if (!p) p = ++cnt;
if (l == r) {
tr[p].f = f, tr[p].m = w;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) update(tr[p].lc, l, mid, x, f, w);
else update(tr[p].rc, mid + 1, r, x, f, w);
pushup(p, l, r);
}
int Merge(int p, int q, int l, int r) {
if (!p || !q) return p + q;
if (l == r) {
tr[p].f += tr[q].f;
tr[p].m = min(tr[p].m, tr[q].m);
return p;
}
int mid = (l + r) >> 1;
tr[p].lc = Merge(tr[p].lc, tr[q].lc, l, mid);
tr[p].rc = Merge(tr[p].rc, tr[q].rc, mid + 1, r);
pushup(p, l, r);
return p;
}
int lm_;
LL qans(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) {
LL ans = query(p, l, r, lm_);
lm_ = min(lm_, tr[p].m);
return ans;
}
int mid = (l + r) >> 1;
LL res = 0;
if (L <= mid) res += qans(tr[p].lc, l, mid, L, R);
if (R > mid) res += qans(tr[p].rc, mid + 1, r, L, R);
return res;
}
void dfs2(int p, int lst) {
for (auto x : add[p]) {
update(rt[0][p], 1, lim, w[x], w[x], x);
update(rt[1][p], 1, lim, lim - w[x] + 1, w[x], x);
}
for (auto x : G[p]) {
if (x != lst) {
dfs2(x, p);
rt[0][p] = Merge(rt[0][p], rt[0][x], 1, lim);
rt[1][p] = Merge(rt[1][p], rt[1][x], 1, lim);
}
}
for (auto x : qry[p]) {
lm_ = x.se;
ans[x.se] += qans(rt[0][p], 1, lim, x.fi, lim);
lm_ = x.se;
ans[x.se] += qans(rt[1][p], 1, lim, lim - x.fi + 1, lim);
lm_ = x.se;
ans[x.se] -= qans(rt[0][p], 1, lim, x.fi, x.fi);
}
for (auto x : del[p]) {
update(rt[0][p], 1, lim, w[x], 0, inf);
update(rt[1][p], 1, lim, lim - w[x] + 1, 0, inf);
}
}
int main() {
n = read(), m = read(), tr[0].m = inf;
for (int i = 1; i < n; i++) {
int x = read(), y = read();
G[x].push_back(y);
G[y].push_back(x);
}
dfs1(1, 0);
for (int i = 1; i <= m; i++) {
op[i] = read();
if (op[i]) {
int u = read(), v = read();
w[i] = read();
int t = lca(u, v);
add[u].push_back(i);
del[t].push_back(i);
if (v != t) {
add[v].push_back(i);
del[jump(v, dep[v] - dep[t] - 1)].push_back(i);
}
}
else {
int x = read(), y = read();
qry[x].push_back({y, i});
}
}
dfs2(1, 0);
for (int i = 1; i <= m; i++) if (!op[i]) printf("%lld\n", ans[i]);
return 0;
}
本文作者:TulipeNoire
本文链接:https://www.cnblogs.com/TulipeNoire/p/18717065/ZZZ
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步