[ABC337G] Tree Inversion(换根 dp + 值域线段树)
题目形式就很换根 dp
如果这种题用朴素的做法求,就是暴力以每个点都做一次根跑树,自底向上统计,时间是
而换根 dp 的思想就是分两步,
一般先钦定某个点(如 1)为根,统计一遍以 1 为根时的结果,
然后挖掘如果以其他点为根时,变换对结果的影响,一般就是自顶向下更新如果换根后的信息
这样分成两次 dfs,时间就是线性的
这个题发现就是求类似树上逆序对的个数,即在同一个子树里,节点编号和深度大小关系相反
考虑第一步,设
显然,我们要知道子树中编号小于
考虑第二步,设
思考由
这里还需要增加一个
需要注意的是,总的小于
对于
也是很重要的一部分,这里是很经典的建值域线段树,或是树状数组,只不过这题是在树上
要求子树
时间是
#include <bits/stdc++.h> #define re register int #define lp p << 1 #define rp p << 1 | 1 #define int long long using namespace std; const int N = 2e5 + 10; struct Edge { int to, next; }e[N << 1]; int idx, h[N]; struct Tree { int l, r, sum; }t[N << 2]; int n, f[N], g[N], a[N], b[N]; inline void add(int x, int y) { e[++ idx] = (Edge){y, h[x]}; h[x] = idx; } inline void build(int p, int l, int r) { t[p].l = l, t[p].r = r; if (l == r) return; int mid = (l + r) >> 1; build(lp, l, mid); build(rp, mid + 1, r); } inline void update(int p, int x, int k) { if (t[p].l == x && t[p].r == x) { t[p].sum += k; return; } int mid = (t[p].l + t[p].r) >> 1; if (x <= mid) update(lp, x, k); if (x > mid) update(rp, x, k); t[p].sum = t[lp].sum + t[rp].sum; } inline int query(int p, int l, int r) { if (l > r) return 0; if (l <= t[p].l && t[p].r <= r) return t[p].sum; int res = 0; int mid = (t[p].l + t[p].r) >> 1; if (l <= mid) res += query(lp, l, r); if (r > mid) res += query(rp, l, r); return res; } void dfs1(int u, int fa) { a[u] = -query(1, 1, u - 1); b[u] = -query(1, 1, fa - 1); for (re i = h[u]; i; i = e[i].next) { int v = e[i].to; if (v == fa) continue; dfs1(v, u); } update(1, u, 1); a[u] += query(1, 1, u - 1); b[u] += query(1, 1, fa - 1); f[u] = a[u]; for (re i = h[u]; i; i = e[i].next) { int v = e[i].to; if (v == fa) continue; f[u] += f[v]; } } void dfs2(int u, int fa) { if (u != 1) g[u] = g[fa] - b[u] + (u - 1 - a[u]); for (re i = h[u]; i; i = e[i].next) { int v = e[i].to; if (v == fa) continue; dfs2(v, u); } } signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; for (re i = 1; i < n; i ++) { int x, y; cin >> x >> y; add(x, y), add(y, x); } build(1, 1, n); dfs1(1, 0); g[1] = f[1]; dfs2(1, 0); for (re i = 1; i <= n; i ++) cout << g[i] << ' '; cout << '\n'; return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战