点分治学习Thinking
点分树学习
Front
看我的上一篇文章!
What?
实际上就是淀粉质的动态(?)版(吧?)
Bing
根据\(\tt Bing\)说,
点分树就是把点分治时“每一层的重心”与“下一层的重心”连起来,构成的一棵新树。这棵树的深度被强行压缩到了 \(O(\log n)\)。
好像看不懂?
What??
换句话说吧
在 fast 函数里,你每次找到重心 c 后,不要只是处理完就走,而是记录一下:他的父亲
- 第一层的重心作为点分树的根。
- 第二层的重心(子树重心)连向第一层的重心。
- 依次类推。
How?
我们手玩发现,虽然在点分树中,谁是爸爸被打乱了 怎么有点像我们的小学 但他们的距离不变。
比如 \((u, v)\) 谁是父亲不重要了,但距离永远是\(dist(u, v)\)!!!
根据淀粉质可得,每一次向下遍历都会砍掉一半,所以高度为\(\log n\)
所以如果点 \(u\) 的值改变了话,修改他的\(fa[u]\), \(fa[fa[u]]\)吗,一直到根节点。就可以了!!!
Why?
为啥要建这棵树?
为什么不能在原树上修改?
如果原树是一条 \(10^5\) 长度的链,你修改叶子节点,影响到的祖先有 \(10^5\) 个,复杂度退化为 \(O(n)\)。 但在点分树中,任何一个点到根的距离不超过 20 层(\(\log_2 10^5 \approx 17\))。好精美!!!!🎇🎇😮😮
总复杂度: \(O(q \log^2 n)\) 豪爽豪爽豪爽豪爽豪爽!
Q&A
为什么要用树状数组?😋😋
点分树把树的深度压到了 \(O(\log n)\),让我们能快速找到受影响的祖先。但每个祖先节点手里都管着一堆距离它远近不一的点。当你问“距离 \(x\) 为 \(K\) 以内”时,这其实是一个前缀和(或者说区间和)查询。 BIT 就是那个能以 \(O(\log (范围))\) 的速度处理这种“前缀和”的最快工具。
实际上,(ZKW)线段树也可以,无非只是常数大了点
为什么要用
LCA
在点分树上“爬楼梯”时,我们频繁需要知道一个点 \(u\) 到它点分树祖先 \(anc\) 之间的原始树距离 \(dist(u, anc)\)。虽然点分树把树结构变了,但题目要求的距离永远是原树上的距离。
而计算原树上两点距离的标准公式是:
所以,没有 LCA,你就无法在点分树上进行任何关于“距离”的计算。
Code
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
vector<int> G[MAXN];
int val[MAXN], temp_val[MAXN];
int n, m;
//SZ2
class BIT
{
private:
vector<int> bits;
inline int lowbit(int x) { return x & -x; }
public:
void init(int n) { bits.assign(n + 5, 0); }
void add(int x, int v)
{
x++;
if (x <= 0)
return;
for (int i = x; i < (int)bits.size(); i += lowbit(i))
bits[i] += v;
}
int query(int x)
{
x++;
if (x <= 0)
return 0;
if (x >= (int)bits.size())
x = (int)bits.size() - 1;
int ans = 0;
for (int i = x; i > 0; i -= lowbit(i))
ans += bits[i];
return ans;
}
} T0[MAXN], T1[MAXN];
// ST & RMQ & LCA
int dep[MAXN], st[MAXN << 1][21], pos[MAXN], dfn_cnt, lg2[MAXN << 1];
void dfs_lca(int u, int f, int d)
{
st[++dfn_cnt][0] = u;
pos[u] = dfn_cnt;
dep[u] = d;
for (int v : G[u])
{
if (v != f)
{
dfs_lca(v, u, d + 1);
st[++dfn_cnt][0] = u;
}
}
}
void init_st()
{
lg2[1] = 0;
for (int i = 2; i <= dfn_cnt; i++)
lg2[i] = lg2[i / 2] + 1;
for (int j = 1; j <= 20; j++)
for (int i = 1; i + (1 << j) - 1 <= dfn_cnt; i++)
{
int a = st[i][j - 1], b = st[i + (1 << (j - 1))][j - 1];
st[i][j] = (dep[a] < dep[b] ? a : b);
}
}
int get_dist(int u, int v)
{
int l = pos[u], r = pos[v];
if (l > r)
swap(l, r);
int k = lg2[r - l + 1];
int a = st[l][k], b = st[r - (1 << k) + 1][k];
int lca = (dep[a] < dep[b] ? a : b);
return dep[u] + dep[v] - 2 * dep[lca];
}
// main
int sz[MAXN], maxs[MAXN], root, tot_sz, pa[MAXN];
bool vis[MAXN];
void get_sz(int u, int f)
{
sz[u] = 1;
for (int v : G[u])
{
if (v != f && !vis[v])
{
get_sz(v, u);
sz[u] += sz[v];
}
}
}
void get_root(int u, int f)
{
sz[u] = 1;
maxs[u] = 0;
for (int v : G[u])
{
if (v != f && !vis[v])
{
get_root(v, u);
sz[u] += sz[v];
maxs[u] = max(maxs[u], sz[v]);
}
}
maxs[u] = max(maxs[u], tot_sz - sz[u]);
if (maxs[u] < maxs[root])
root = u;
}
void build(int u, int f, int s)
{
vis[u] = true;
pa[u] = f;
T0[u].init(s);
T1[u].init(s);
for (int v : G[u])
{
if (!vis[v])
{
get_sz(v, 0);
tot_sz = sz[v];
root = 0;
maxs[0] = MAXN;
get_root(v, 0);
int next_root = root;
build(next_root, u, tot_sz);
}
}
}
void update(int u, int x)
{
int delta = x - val[u];
val[u] = x;
for (int i = u; i; i = pa[i])
{
T0[i].add(get_dist(u, i), delta);
if (pa[i])
T1[i].add(get_dist(u, pa[i]), delta);
}
}
int query(int u, int k)
{
int res = T0[u].query(k);
for (int i = u; pa[i]; i = pa[i])
{
int d = get_dist(u, pa[i]);
if (k >= d)
{
res += T0[pa[i]].query(k - d);
res -= T1[i].query(k - d);
}
}
return res;
}
// solve
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> temp_val[i];
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs_lca(1, 0, 0);
init_st();
root = 0;
maxs[0] = MAXN;
tot_sz = n;
get_root(1, 0);
int first_root = root;
build(first_root, 0, n);
for (int i = 1; i <= n; i++)
update(i, temp_val[i]);
int lastans = 0;
while (m--)
{
int opt, x, y;
cin >> opt >> x >> y;
x ^= lastans;
y ^= lastans;
if (opt == 0)
{
lastans = query(x, y);
cout << lastans << "\n";
}
else
{
update(x, y);
}
}
}
signed main(void)
{
// freopen("name.in", "r", stdin);
// freopen("name.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}

浙公网安备 33010602011771号