CF276E Little Girl and Problem on Trees(线段树+树状数组)

Little Girl and Problem on Trees

题意:

给定一棵无边权的树,除了根节点以外的节点度数不超过 2,有两种操作

  • 0 v x d)将距离 u 节点 d 距离之内的节点的值加上 x
  • 1 v)询问 u 节点的值

n100000q100000

思路:

通过两个操作的描述,可以知道要实现的是区间修改单点查询两个操作,不难想到要用线段树来实现.

//线段树 struct Node { int tag, len; i64 sum; } tr[N << 2]; void pull(int u) { tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; } void build(int u, int l, int r) { tr[u].len = r - l + 1; if (l == r) return ; int mid = l + r >> 1; build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); } void settag(int u, int x) { tr[u].sum += x * tr[u].len; tr[u].tag += x; } void push(int u) { if (!tr[u].tag) return ; settag(u << 1, tr[u].tag); settag(u << 1 | 1, tr[u].tag); tr[u].tag = 0; } void modify(int u, int l, int r, int ln, int rn, int x) { if (l >= ln && r <= rn) return void(settag(u, x)); int mid = (l + r) >> 1; push(u); if (mid >= ln) modify(u << 1, l, mid, ln, rn, x); if (mid < rn) modify(u << 1 | 1, mid + 1, r, ln, rn, x); pull(u); } int query(int u, int l, int r, int pos) { if (l == r) return tr[u].sum; int mid = (l + r) >> 1; push(u); if (mid >= pos) return query(u << 1, l, mid, pos); else return query(u << 1 | 1, mid + 1, r, pos); }

但是这是在树上,所以很自然的就想到将整棵树剖成一个序列,用 dfs 序来表示这个树。

struct node { int v, nxt; }e[N * 2]; void connect(int a, int b) { e[++idx] = {b, h[a]}, h[a] = idx; } void dfs(int u, int fa) { dep[u] = dep[fa] + 1; top[u] = u; dfn[u] = ++cur; for (int i = h[u]; i; i = e[i].nxt) { if (e[i].v == fa) continue; dfs(e[i].v, u); if (dep[top[e[i].v]] > dep[top[u]]) top[u] = top[e[i].v]; } }

接下来要考虑操作一中距离节点 u 距离为 d 的所有节点都加上 x ,这个包括的范围是在 dfn[u]±d 所以修改的区间范围就出来了。但是不能够直接对 [dfnud,dfnu+d] 这个区间进行区间修改。由于题目中说的除了根节点之外的所有的点度数不超过 2 所以可以知道所有的点都是在各自的链上,树大致长这个样, 其中 1 是根节点。
img

那么对于距离 d 就会出现 dfnu+d 超过了这个链的长度,此时就要将右边界限制在这条链最深的节点的深度。再考虑左边界,如果 depud>1 那么左边界就还是在这一条链上;但如果 depud<0 就会出现离节点 u 距离为 d 的点出现在另外一条链上,这样的话线段树就很难操作了,因为其他所有的链都是要加上 x 的。那么可以考虑将需要加在所有链上的贡献归结到根节点上,将距离根节点 ddepu 的所有点都加上 x ,这个操作可以等同于单点加法,将所有点的深度作为区间,维护前缀和,基于这一点可以用树状数组来维护.

void add(int x, int v) { for (int i = x; i <= n; i += i & -i) c[i] += v; } i64 ask(int x) { i64 ans = 0; for (int i = x; i; i -= i & -i) ans += c[i]; return ans; } i64 range(int l, int r) { return ask(r) - ask(l); }

每一次查询的结果就是 trntrdepu1

signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); std::cin >> n >> q; for (int i = 1; i < n; i++) { int u, v; std::cin >> u >> v; connect(u, v), connect(v, u); } dfs(1, 0); build(1, 1, n); for (int i = 0; i < q; i++) { int op, v; std::cin >> op >> v; if (op == 0) { int x, d; std::cin >> x >> d; if (v == 1) { add(d + 1, x); continue; } int l = 0, r = dfn[v] + std::min(d, dep[top[v]] - dep[v]); if (dep[v] - 1 > d) { l = dfn[v] - d; } else { l = dfn[v] - (dep[v] - 2); int p = d - dep[v] + 1; modify(1, 1, n, l, std::min(p + l - 1, dfn[top[v]]), -x); add(p + 1, x); } modify(1, 1, n, l, r, x); } else { i64 ans = query(1, 1, n, dfn[v]); ans += range(dep[v] - 1, n); std::cout << ans << "\n"; } } return 0 ^ 0; }

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16776017.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示