[模板] 点分树
前言
确实是树上问题中一个非常神奇的部分
好好学一下
思路
点分树的性质其实还是比较好理解, 这里重点解释点分树的两棵段树是怎么回事, 具体如何维护
首先要知道的是, 维护两棵段树的目的是为了去除同子树中的贡献
贡献计算(查询)
- 表示点分树上的父子关系
- 表示点 在点分树上的子树集合
- 表示点 在点分树上的父树集合
- 表示点 和点 在点分树上的距离
- 表示点 的点权
其中
- 式表示重构树上 的子树中与 距离小于等于 的点权值之和
- 式表示重构树上 的子树中与 距离小于等于 的点权值之和
如何利用?
假设当前查询 , 即查询所有距离 小于等于 的点权和
对于重构树上的父子节点 , 即除了 子树外面的部分 对答案的贡献为
解释一下, 我们考虑只在重构树上的 处统计 的贡献一个性质是 重构树上的 一定是 的必经之路, 也就是说如果枚举 , 那么满足条件的 必定满足
不难发现 , 考虑枚举 , 借以统计答案
上面计算的 的意义就是枚举 , 的贡献
但是还是有问题, 不难发现如果 在 的同一个子树之中, 那么其 必然不等于 , 那么 的贡献就会重复, 所以需要用以下操作去重
- :
表示重构树上点 的子树集合中, 距离 小于等于 的点权和 - :
发现上一个式子包含了两点同在一个子树内的情况, 所以需要减去重构树上点 的子树中, 距离 小于等于 的点权和
最终的答案就是
贡献更新(修改)
考虑修改的情况, 其实跟上面很像, 按照定义修改 和
具体怎么做?
对于这个点你往上暴力跳 , 然后更新即可
所以我们现在需要维护单点修改, 询问前缀, 不难发现可以用动态开点线段树处理
用树链剖分维护 \(\rm{LCA}\) 即可
代码
#include <bits/stdc++.h>
const int MAXN = 1e5 + 20;
int n, m;
int w[MAXN];
/*动态开点线段树*/
struct DSTree {
struct node { int ls, rs; int val; node() { ls = rs = val = 0; } }; std::vector<node> Tree;
int cnt, rt;
DSTree() { cnt = 0, rt = 0; }
/*----------------------------------------*/
void pushup(int rt) { Tree[rt].val = Tree[Tree[rt].ls].val + Tree[Tree[rt].rs].val; }
// void pushdown()
// void addtag()
/*----------------------------------------*/
void build(int sz) { Tree.resize((sz + 1) * 6); }
void update(int& rt, int l, int r, int val, int aim) {
if (!rt) rt = ++cnt;
if (l == r) { Tree[rt].val += val; return; }
int mid = (l + r) >> 1;
if (aim <= mid) update(Tree[rt].ls, l, mid, val, aim);
else update(Tree[rt].rs, mid + 1, r, val, aim);
pushup(rt);
}
int query(int rt, int l, int r, int L, int R) {
if (!rt) return 0;
if (L <= l && r <= R) return Tree[rt].val;
int mid = (l + r) >> 1, ans = 0;
if (L <= mid) ans += query(Tree[rt].ls, l, mid, L, R);
if (R > mid) ans += query(Tree[rt].rs, mid + 1, r, L, R);
return ans;
}
};
/*树*/
struct graph {
struct node { int to, nxt; } edge[MAXN << 1]; int head[MAXN], cnt = -1;
void head_init() { memset(head, -1, sizeof head); }
void addedge(int u, int v) { edge[++cnt] = (node) { v, head[u] }; head[u] = cnt; }
int tmp; // dfn 辅助
graph() { tmp = 0, head_init(); }
struct point {
DSTree f, g;
int dep, fa, siz, Hson, dfn, top;
int mxt;
} dot[MAXN];
} tree, retr;
/*树链剖分*/
class TreeDivder {
private:
void dfs1(int u, int fat) {
tree.dot[u].dep = tree.dot[fat].dep + 1, tree.dot[u].fa = fat, tree.dot[u].siz = 1;
for (int e = tree.head[u]; ~e; e = tree.edge[e].nxt) {
int v = tree.edge[e].to; if (v == fat) continue;
dfs1(v, u); tree.dot[u].siz += tree.dot[v].siz;
if (!tree.dot[u].Hson || tree.dot[v].siz > tree.dot[tree.dot[u].Hson].siz) tree.dot[u].Hson = v;
}
}
void dfs2(int u, int topu) {
tree.dot[u].dfn = ++tree.tmp, tree.dot[u].top = topu;
if (tree.dot[u].Hson) dfs2(tree.dot[u].Hson, topu);
for (int e = tree.head[u]; ~e; e = tree.edge[e].nxt) {
int v = tree.edge[e].to; if (v == tree.dot[u].fa || v == tree.dot[u].Hson) continue;
dfs2(v, v);
}
}
public:
void init(int rt) { tree.dot[rt].dep = 1; dfs1(rt, 0); dfs2(rt, rt); }
int LCA(int u, int v) {
while (tree.dot[u].top != tree.dot[v].top) {
/*让链头更深的先跳*/ if (tree.dot[tree.dot[u].top].dep < tree.dot[tree.dot[v].top].dep) std::swap(u, v);
u = tree.dot[tree.dot[u].top].fa;
}
return (tree.dot[u].dep < tree.dot[v].dep) ? u : v;
}
int dis(int u, int v) { return tree.dot[u].dep + tree.dot[v].dep - 2 * tree.dot[LCA(u, v)].dep; }
} diver;
/*重构树*/
class TreeRestructer {
private:
int done[MAXN], sum; // 已经处理完的部分
public:
TreeRestructer(int x) { sum = x; memset(done, false, sizeof done); }
/*获取该连通块的重心*/
void getrt(int u, int fat, int& rt) {
tree.dot[u].siz = 1, tree.dot[u].mxt = 0;
for (int e = tree.head[u]; ~e; e = tree.edge[e].nxt) {
int v = tree.edge[e].to; if (v == fat || done[v]) continue;
getrt(v, u, rt); tree.dot[u].siz += tree.dot[v].siz;
tree.dot[u].mxt = std::max(tree.dot[u].mxt, tree.dot[v].siz);
}
tree.dot[u].mxt = std::max(tree.dot[u].mxt, sum - tree.dot[u].siz);
if (tree.dot[u].mxt <= tree.dot[rt].mxt || !rt) rt = u;
}
/*建立重构树*/
void build(int rt, int fat) {
done[rt] = true; int subsize = sum;
retr.dot[rt].fa = fat;
retr.dot[rt].f.build(subsize / 2 + 1), retr.dot[rt].g.build(subsize + 1);
retr.dot[rt].siz = subsize;
for (int e = tree.head[rt]; ~e; e = tree.edge[e].nxt) {
int v = tree.edge[e].to; if (done[v]) continue;
sum = tree.dot[v].siz > tree.dot[rt].siz ? subsize - tree.dot[rt].siz : tree.dot[v].siz;
int x; tree.dot[x = 0].mxt = 0x3f3f3f3f;
getrt(v, rt, x); build(x, rt);
}
}
};
/*处理问题*/
class AnsGiver {
private:
void modify(int idx, int val) {
int now = idx;
while (~now) {
int fa = retr.dot[now].fa;
retr.dot[now].f.update(retr.dot[now].f.rt, 0, retr.dot[now].siz, val, diver.dis(now, idx));
if (~fa) retr.dot[now].g.update(retr.dot[now].g.rt, 0, retr.dot[fa].siz, val, diver.dis(fa, idx));
now = fa;
}
}
int query(int idx, int k) {
int res = 0;
int now = idx, last = 0;
while (~now) {
int d = diver.dis(idx, now);
if (d > k) { last = now, now = retr.dot[now].fa; continue; } // 注意是 continue
res += retr.dot[now].f.query(retr.dot[now].f.rt, 0, retr.dot[now].siz, 0, k - d);
if (last) res -= retr.dot[last].g.query(retr.dot[last].g.rt, 0, retr.dot[now].siz, 0, k - d);
last = now, now = retr.dot[now].fa;
}
return res;
}
public:
int lastans = 0;
AnsGiver() { lastans = 0; }
void decode(int& x, int& y) { x ^= lastans, y ^= lastans; }
void solve() {
for (int i = 1; i <= n; i++) modify(i, w[i]);
for (int i = 1, op, x, y; i <= m; i++) {
scanf("%d %d %d", &op, &x, &y); decode(x, y);
if (op) { modify(x, y - w[x]); w[x] = y; }
else { printf("%d\n", lastans = query(x, y)); }
}
}
} ag;
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
tree.head_init(); for (int i = 1, u, v; i < n; i++) scanf("%d %d", &u, &v), tree.addedge(u, v), tree.addedge(v, u);
/*先处理树链剖分*/ diver.init(1);
/*建立重构树*/ TreeRestructer tr(n); int rt; tree.dot[rt = 0].mxt = 0x3f3f3f3f; tr.getrt(1, -1, rt), tr.build(rt, -1);
ag.solve();
return 0;
}
不知道 \(\rm{MLE}\) 是什么造成的, 后面再看

浙公网安备 33010602011771号