点分树(最暴力的一集)
(维什戴尔本色出演)
前情提要
首先,你得学会点分治
引入
当你学会点分治,你会意识到点分治更多的是一种静态的问题
那么对于P6329 【模板】点分树 | 震波这道题,你会显得很无奈,它竟然要动态修改权值
P6329 【模板】点分树 | 震波
那么好,当我们意识到问题后我们就可以开始思考了,考虑到它的树结构是不变的,那么我们可不可以通过存储树结构来解决点分治的静态问题呢?
那么我们的具体时间就变成了:用点分治求出树结构,然后用点分树来解决答案问题
答案如何求
对于一个树结构,我么该如何求答案?
我们很快能想到,因为对于一个查询节点它曾经肯定是一个重心,所以我们可以计算以它为重心时,以它为线段开头的答案,不过,经过它的答案需要它的祖宗来计算。
这下犯了难,假如我们是点分治我们该如何做?
对于祖先节点,当当前节点的设定余波(这里设定余波范围就是以当前节点为子树的根时当前子树的直径的一半)可以波及到祖先节点,那么祖先节点的下一个重心肯定不是他,也就是说在设定余波的范围内不会有自己的祖先(祖宗发射器!!!)考虑设定余波的增长,我们发现按照点分治的树结构,我们的设定余波是两倍两倍增长的,假设真实余波(输入的余波)在设定余波范围内,那么祖先就不可能影响到自己,那么由于设定余波为两倍增长,假设我们暴力跳祖先,我们很快就能想到这是一个倍增,所以时间复杂度是log级别的
最后一个问题,我们暴力跳祖宗,计算答案时需要剪掉跳过来前的节点答案,以防计算bug。
不会吧,还不会?你点分治怎么学的?我不就是用树状数组等数据结构维护这个查找的答案吗?
代码
#include <iostream> #include <vector> using namespace std; const int MaxN = 2e5 + 10, MaxK = 19; int f[MaxN][MaxK], gfa[MaxN], dfn[MaxN], lg[MaxN], dep[MaxN], sz[MaxN], a[MaxN], cnt, n, m, cn, minx, rt; vector<int> d[2][MaxN], g[MaxN]; bool vis[MaxN]; int lb(int x) { return x & (-x); } void update(int p, int op, int k, int x) { for (k++; k <= sz[p]; k += lb(k)) { d[op][p][k] += x; } } int query(int p, int op, int k, int res = 0) { for (k = min(sz[p], k + 1); k; k -= lb(k)) { res += d[op][p][k]; } return res; } int mindep(int a, int b) { return dep[a] < dep[b] ? a : b; } void DFS(int x, int fa = 0) { f[++cnt][0] = x, dfn[x] = cnt; for (int i : g[x]) { if (i == fa) continue; dep[i] = dep[x] + 1, DFS(i, x), f[++cnt][0] = x; } } void find(int x, int fa = 0, int maxsz = 0) { sz[x] = 1; for (int i : g[x]) { if (vis[i] || i == fa) continue; find(i, x), sz[x] += sz[i], maxsz = max(maxsz, sz[i]); } maxsz = max(maxsz, n - sz[x]); (maxsz < minx) && (minx = maxsz, rt = x); } void S(int x) { vis[x] = 1, sz[x] = n + 1, d[0][x].resize(sz[x] + 1), d[1][x].resize(sz[x] + 1); for (int i : g[x]) { if (vis[i]) continue; n = sz[i], minx = 1e9, find(i), gfa[rt] = x; S(rt); } } int dis(int x, int y) { if (dfn[x] > dfn[y]) swap(x, y); int dx = dfn[x], dy = dfn[y], len = dy - dx + 1, lca = mindep(f[dx][lg[len]], f[dy - (1 << lg[len]) + 1][lg[len]]); return dep[x] + dep[y] - dep[lca] * 2; } void modify(int x, int w) { for (int i = x; i; i = gfa[i]) { update(i, 0, dis(x, i), w); } for (int i = x; gfa[i]; i = gfa[i]) { update(i, 1, dis(x, gfa[i]), w); } } int main() { ios::sync_with_stdio(0), cin.tie(0); cin >> n >> m, cn = n; for (int i = 1; i <= n; i++) { cin >> a[i]; } for (int i = 1, u, v; i < n; i++) { cin >> u >> v; g[u].push_back(v), g[v].push_back(u); } DFS(1); for (int j = 2; j <= cnt + 1; j++) { lg[j] = lg[j >> 1] + 1; } for (int j = 1; j < MaxK; j++) { for (int i = 1; i + (1 << j) - 1 <= cnt; i++) { f[i][j] = mindep(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); } } minx = 1e9, find(1), S(rt); for (int i = 1; i <= cn; i++) { modify(i, a[i]); } for (int i = 1, op, x, k, lst = 0; i <= m; i++) { cin >> op >> x >> k, x ^= lst, k ^= lst; if (op) { modify(x, k - a[x]), a[x] = k; } else { lst = query(x, 0, k); for (int i = x; gfa[i]; i = gfa[i]) { (k >= dis(x, gfa[i])) && (lst += query(gfa[i], 0, k - dis(x, gfa[i])) - query(i, 1, k - dis(x, gfa[i]))); } cout << lst << '\n'; } } return 0; }
P6626 [省选联考 2020 B 卷] 消息传递
那么这道题就很简单了,只是k内,变成了第k
代码
#include <iostream> #include <vector> using namespace std; const int MaxN = 2e5 + 10, MaxK = 19; int f[MaxN][MaxK], gfa[MaxN], dfn[MaxN], lg[MaxN], dep[MaxN], sz[MaxN], cnt, n, m, cn, minx, t, rt; vector<int> d[2][MaxN], g[MaxN]; bool vis[MaxN]; int lb(int x) { return x & (-x); } void update(int p, int op, int k, int x) { (k <= sz[p]) && (d[op][p][k] += x); } int query(int p, int op, int k, int res = 0) { (k <= sz[p]) && (res += d[op][p][k]); return res; } int mindep(int a, int b) { return dep[a] < dep[b] ? a : b; } void DFS(int x, int fa = 0) { f[++cnt][0] = x, dfn[x] = cnt; for (int i : g[x]) { if (i == fa) continue; dep[i] = dep[x] + 1, DFS(i, x), f[++cnt][0] = x; } } void find(int x, int fa = 0, int maxsz = 0) { sz[x] = 1; for (int i : g[x]) { if (vis[i] || i == fa) continue; find(i, x), sz[x] += sz[i], maxsz = max(maxsz, sz[i]); } maxsz = max(maxsz, n - sz[x]); (maxsz < minx) && (minx = maxsz, rt = x); } void S(int x) { vis[x] = 1, sz[x] = n + 1, vector<int>().swap(d[0][x]), vector<int>().swap(d[1][x]), d[0][x].resize(sz[x] + 1), d[1][x].resize(sz[x] + 1); for (int i : g[x]) { if (vis[i]) continue; n = sz[i], minx = 1e9, find(i), gfa[rt] = x; S(rt); } } int dis(int x, int y) { if (dfn[x] > dfn[y]) swap(x, y); int dx = dfn[x], dy = dfn[y], len = dy - dx + 1, lca = mindep(f[dx][lg[len]], f[dy - (1 << lg[len]) + 1][lg[len]]); return dep[x] + dep[y] - dep[lca] * 2; } void modify(int x, int w) { for (int i = x; i; i = gfa[i]) { update(i, 0, dis(x, i), w); } for (int i = x; gfa[i]; i = gfa[i]) { update(i, 1, dis(x, gfa[i]), w); } } void clear() { for (int i = 1; i <= n; i++) { vector<int>().swap(g[i]); vis[i] = gfa[i] = dfn[i] = dep[i] = sz[i] = 0; d[0][i].clear(), d[1][i].clear(); } } int main() { ios::sync_with_stdio(0), cin.tie(0); for (int j = 2; j < MaxN; j++) { lg[j] = lg[j >> 1] + 1; } for (cin >> t; t; t--) { cin >> n >> m, cn = n, cnt = 0; clear(); for (int i = 1, u, v; i < n; i++) { cin >> u >> v; g[u].push_back(v), g[v].push_back(u); } DFS(1); for (int j = 1; j < MaxK; j++) { for (int i = 1; i + (1 << j) - 1 <= cnt; i++) { f[i][j] = mindep(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); } } minx = 1e9, find(1), S(rt); for (int i = 1; i <= cn; i++) { modify(i, 1); } for (int i = 1, x, k, ans = 0; i <= m; i++) { cin >> x >> k; ans = query(x, 0, k); for (int i = x; gfa[i]; i = gfa[i]) { int len = k - dis(x, gfa[i]); (len >= 0) && (ans += query(gfa[i], 0, len) - query(i, 1, len)); } cout << ans << '\n'; } } return 0; }
一些对于点分树的幻想
P10930 异象石
一开始看到这个题,我会觉得很熟悉,所以我们思考到了点分树,可是,过于暴力,导致这个是失败的
本文作者:yabnto
本文链接:https://www.cnblogs.com/ybtarr/p/18736245
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步