【luogu P3384】【模板】轻重链剖分

【模板】轻重链剖分

题目链接:luogu P3384

题目大意

有一个树,点有权值,要你维护一些操作。
把两点间的路径中的点的点权都加一个值或者询问和。
把一个点对应的子树中点的点权都加一个值或者询问和。

根给出,输出的和对一个给出的数取模。

思路

这道题也是树链剖分的模板题,之前写过一道,树链剖分怎么做就不写了。
——>点这里看<——

但是这题比那一道有一个新的东西,就是会对一个点对于的子树进行操作。
那这怎么办呢?
我们考虑子树中的点在线段树中的位置,通过观察我们建线段树的过程。那你是 dfs 来标号的,那一个子树的点肯定是连续的一段,而且开头的是这个子树的根节点,也就是这给点。
那它在线段树中就是一个区间,设这个子树根节点为 root,大小(节点个数)为 sizeroot,然后树中的点 i 在线段树中的位置是 dyi,那线段树对于它的区间就是 dyrootdyroot+sizeroot1

那就可以了。
记得取模。

代码

#include<cstdio> #include<cstring> #include<iostream> using namespace std; struct node { int to, nxt; }e[200001]; int n, x, y, z, le[100001], KK, tot, q, root, mo; int number[100001], top[100001], tree_pl[100001], normal_pl[400001]; int fa[100001], num[100001], deg[100001], son[100001]; long long sum[400001], Sum, lazy[400001]; int op, size[100001]; void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; } void dfs1(int now, int father) { fa[now] = father; deg[now] = deg[father] + 1; size[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (father != e[i].to) { dfs1(e[i].to, now); size[now] += size[e[i].to]; if (size[e[i].to] > size[son[now]]) son[now] = e[i].to; } } void dfs2(int now, int father) { if (son[now]) { tree_pl[son[now]] = ++tot; top[son[now]] = top[now]; normal_pl[tree_pl[son[now]]] = son[now]; dfs2(son[now], now); } for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father && e[i].to != son[now]) { tree_pl[e[i].to] = ++tot; top[e[i].to] = e[i].to; normal_pl[tree_pl[e[i].to]] = e[i].to; dfs2(e[i].to, now); } } void up(int now) { sum[now] = sum[now << 1] + sum[now << 1 | 1]; sum[now] %= mo; } void down(int now, int l, int r) { int mid = (l + r) >> 1; sum[now << 1] += lazy[now] * (mid - l + 1) % mo; sum[now << 1] %= mo; sum[now << 1 | 1] += lazy[now] * (r - (mid + 1) + 1) % mo; sum[now << 1 | 1] %= mo; lazy[now << 1] += lazy[now]; lazy[now << 1] %= mo; lazy[now << 1 | 1] += lazy[now]; lazy[now << 1 | 1] %= mo; lazy[now] = 0; } void build(int now, int l, int r) { if (l == r) { sum[now] = number[normal_pl[l]] % mo; return ; } int mid = (l + r) >> 1; build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r); up(now); } void add_num(int now, int l, int r, int L, int R, int add__num) { if (l > R || r < L) return ; if (l >= L && r <= R) { sum[now] = (sum[now] + ((r - l + 1) * add__num) % mo) % mo; if (l != r) lazy[now] += add__num; return ; } down(now, l, r); int mid = (l + r) >> 1; if (L <= mid) add_num(now << 1, l, mid, L, R, add__num); if (mid + 1 <= R)add_num(now << 1 | 1, mid + 1, r, L, R, add__num); up(now); } void query(int now, int l, int r, int L, int R) { if (r < L || l > R) return ; if (l >= L && r <= R) { Sum += sum[now]; Sum %= mo; return ; } down(now, l, r); int mid = (l + r) >> 1; if (L <= mid) query(now << 1, l, mid, L, R); if (mid + 1 <= R) query(now << 1 | 1, mid + 1, r, L, R); } void ask(int x, int y) { while (top[x] != top[y]) { if (deg[top[x]] < deg[top[y]]) { swap(x, y); } query(1, 1, tot, tree_pl[top[x]], tree_pl[x]); x = fa[top[x]]; } if (deg[x] > deg[y]) swap(x, y); query(1, 1, tot, tree_pl[x], tree_pl[y]); } void insert(int x, int y, int z) { while (top[x] != top[y]) { if (deg[top[x]] < deg[top[y]]) { swap(x, y); } add_num(1, 1, tot, tree_pl[top[x]], tree_pl[x], z); x = fa[top[x]]; } if (deg[x] > deg[y]) swap(x, y); add_num(1, 1, tot, tree_pl[x], tree_pl[y], z); } int main() { scanf("%d %d %d %d", &n, &q, &root, &mo); for (int i = 1; i <= n; i++) scanf("%d", &number[i]); for (int i = 1; i < n; i++) { scanf("%d %d", &x, &y); add(x, y); add(y, x); } dfs1(root, 0); tot = 1; top[root] = root; tree_pl[root] = 1; normal_pl[1] = root; dfs2(root, 0); build(1, 1, tot); for (int i = 1; i <= q; i++) { scanf("%d", &op); if (op == 1) { scanf("%d %d %d", &x, &y, &z); insert(x, y, z); } else if (op == 2) { scanf("%d %d", &x, &y); Sum = 0; ask(x, y); printf("%lld\n", Sum); } else if (op == 3) { scanf("%d %d", &x, &y); add_num(1, 1, tot, tree_pl[x], tree_pl[x] + size[x] - 1, y); //某个点对应子树在线段树中的位置=它在线段树中的位置~它的位置+它子树的节点个数-1 //下面同理 } else if (op == 4) { scanf("%d", &x); Sum = 0; query(1, 1, tot, tree_pl[x], tree_pl[x] + size[x] - 1); printf("%lld\n", Sum); } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P3384.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(78)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示