📅  最后修改于: 2023-12-03 14:55:53.526000             🧑  作者: Mango
欧拉之旅是一种基于欧拉回路和欧拉序列的树上技巧,常常被用于树上统计问题的解决。而使用细分树的子树总和,则是欧拉之旅的一种变体实现,它能够高效地处理子树和的统计问题。
细分树其实就是一棵带权树状数组,每个节点记录当前子树内的所有节点的权值和。通过先进行一次欧拉遍历,获取每个节点的入栈和出栈时间戳,并在细分树上维护这两个值之间的区间,即可快速地计算出任意一个节点的子树和。
下面是使用C++实现的细分树的子树总和计算方法:
vector<int> head, nxt, to, w;
int idx;
inline void add(int u, int v, int val) {
to[++idx] = v, w[idx] = val;
nxt[idx] = head[u];
head[u] = idx;
}
int dep[N], siz[N], fa[N], son[N], top[N], dfn[N], ts;
void dfs1(int x) {
siz[x] = 1;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == fa[x]) continue;
dep[y] = dep[x] + 1, fa[y] = x;
dfs1(y), siz[x] += siz[y];
if (siz[y] > siz[son[x]]) son[x] = y;
}
}
void dfs2(int x, int tp) {
top[x] = tp, dfn[x] = ++ts;
if (son[x]) dfs2(son[x], tp);
for (int i = head[x], y; i; i = nxt[i]) {
y = to[i];
if (y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
}
struct SegTree {
int l, r;
ll s, tag;
#define getmid int mid = (l + r) >> 1, ls = o << 1, rs = o << 1 | 1
#define lsq ls, l, mid
#define rsq rs, mid+1, r
#define pushup s = tr[ls].s + tr[rs].s
#define pushdown tr[ls].tag += tr[o].tag, tr[ls].s += (mid-l+1) * tr[o].tag, tr[rs].tag += tr[o].tag, tr[rs].s += (r-mid) * tr[o].tag, tr[o].tag = 0;
#define chk if (!o) o = ++tott, tr[o].l = l, tr[o].r = r
}sgt[N << 2]; int tott;
void modify(int o, int l, int r, int ql, int qr, int val) {
if (ql <= l && r <= qr) {
sgt[o].s += (r-l+1) * val, sgt[o].tag += val;
return;
}
getmid, chk;
if (ql <= mid) modify(lsq, ql, qr, val);
if (mid < qr) modify(rsq, ql, qr, val);
pushup;
}
ll query(int o, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return sgt[o].s;
getmid, chk, pushdown;
ll res = 0;
if (ql <= mid) res += query(lsq, ql, qr);
if (mid < qr) res += query(rsq, ql, qr);
return res;
}
ll query_path(int x, int y) {
ll res = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
res += query(1, 1, n, dfn[top[x]], dfn[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
res += query(1, 1, n, dfn[x], dfn[y]);
return res;
}
void modify_subtree(int x, int val) {
modify(1, 1, n, dfn[x], dfn[x] + siz[x] - 1, val);
}
使用上述代码,我们可以用modify_subtree(x, val)函数修改节点x及其子树的权值,用query_path(x, y)函数查询节点x到y路径上的所有节点的权值和。其中x与y分别为要查询的起点和终点。
细分树的子树总和是欧拉之旅的一种变体使用方法,它可以高效地处理子树和的统计问题。使用细分树的代码实现并不复杂,但需要理解其中细节并仔细思考。