P3384——树链剖分&&模板
题目描述
如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和
操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和
解决方法
用链式前向星的方式保存树,两次DFS将树剖分成若干重链和轻链,套用线段树进行更新和查询,对子树的操作可以转化成连续节点间的操作(因为DFS时子树节点的编号也是连续的),注意取模和开$long \ \ long$.
而且单独$add$标记时是不用下推的,只需查询时累加即可(不知道为什么那些题解都用下推的)
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- #define lc o <<1
- #define rc o <<1 | 1
- //const ll INF = 0x3f3f3f3f;
- const int maxn = + ;
- struct Edge
- {
- int to, next;
- }edges[*maxn];
- int head[maxn];
- int cur, f[maxn], deep[maxn], size[maxn], son[maxn], rk[maxn], id[maxn], top[maxn], cnt;
- int n, q, w[maxn], root, mod;
- inline void addedge(int u, int v)
- {
- ++cur;
- edges[cur].next = head[u];
- head[u] = cur;
- edges[cur].to = v;
- }
- struct SegTree{
- ll sum[maxn << ], addv[maxn << ];
- void build(int o, int l, int r)
- {
- if(l == r)
- {
- sum[o] = w[rk[l]] % mod;
- }
- else
- {
- int mid = (l + r) >> ;
- build(lc, l, mid);
- build(rc, mid+, r);
- sum[o] = (sum[lc] + sum[rc]) % mod;
- }
- }
- void maintain(int o, int l, int r)
- {
- if(l == r) //如果是叶子结点
- sum[o] = w[rk[l]] % mod;
- else //如果是非叶子结点
- sum[o] = (sum[lc] + sum[rc]) % mod;
- sum[o] = (sum[o] + addv[o] * (r-l+)) % mod;
- }
- //区间修改,[cl,cr] += v;
- void update(int o, int l, int r, int cl, int cr, int v) //
- {
- if(cl <= l && r <= cr) addv[o] = (addv[o] + v) % mod;
- else
- {
- int m = l + (r-l) /;
- if(cl <= m) update(lc, l, m, cl, cr, v);
- if(cr > m) update(rc, m+, r, cl, cr, v);
- }
- maintain(o, l, r);
- }
- //区间查询,sum{ql,qr}
- ll query(int o, int l,int r, ll add, int ql, int qr)
- {
- if(ql <= l && r <= qr)
- {
- //prllf("sum[o]:%d %d*(%d-%d+1)\n", sum[o], add, r, l);
- return (sum[o] + add * (r-l+)) % mod; //tx l-r+1
- }
- else
- {
- int m = l + (r - l) / ;
- ll ans = ;
- add = (add + addv[o]) % mod;
- if(ql <= m) ans = (ans + query(lc, l, m, add, ql, qr)) % mod;
- if(qr > m) ans = (ans + query(rc, m+, r, add, ql, qr)) % mod;
- return ans;
- }
- }
- }st;
- void dfs1(int u, int fa, int depth) //当前节点、父节点、层次深度
- {
- //prllf("u:%d fa:%d depth:%d\n", u, fa, depth);
- f[u] = fa;
- deep[u] = depth;
- size[u] = ; //这个点本身的size
- for(int i = head[u];i;i = edges[i].next)
- {
- int v = edges[i].to;
- if(v == fa) continue;
- dfs1(v, u, depth+);
- size[u] += size[v]; //子节点的size已被处理,用它来更新父节点的size
- if(size[v] > size[son[u]]) son[u] = v; //选取size最大的作为重儿子
- }
- }
- void dfs2(int u, int t) //当前节点、重链顶端
- {
- //prllf("u:%d t:%d\n", u, t);
- top[u] = t;
- id[u] = ++cnt; //标记dfs序
- rk[cnt] = u; //序号cnt对应节点u
- if(!son[u]) return; //没有儿子?
- dfs2(son[u], t); //我们选择优先进入重儿子来保证一条重链上各个节点dfs序连续
- for(int i = head[u];i;i = edges[i].next)
- {
- int v = edges[i].to;
- if(v != son[u] && v != f[u]) dfs2(v, v); //这个点位于轻链顶端,那么它的top必然为它本身
- }
- }
- /*修改和查询的原理是一致的,以查询操作为例,其实就是个LCA,不过这里要使用top数组加速,因为top可以直接跳到该重链的起始顶点*/
- /*注意,每次循环只能跳一次,并且让结点深的那个跳到top的位置,避免两者一起跳而插肩而过*/
- ll querysum(int x, int y)
- {
- int fx = top[x], fy = top[y];
- ll ans = ;
- while(fx != fy) //当两者不在同一条重链上
- {
- if(deep[fx] >= deep[fy])
- {
- //prllf("%d %d\n", id[fx], id[x]);
- ans = (ans + st.query(, , n, , id[fx], id[x])) % mod; //线段树区间求和,计算这条重链的贡献
- x = f[fx]; fx = top[x];
- }
- else
- {
- //prllf("%d %d\n", id[fy], id[y]);
- ans = (ans + st.query(, , n, , id[fy], id[y])) % mod;
- y = f[fy]; fy = top[y];
- }
- }
- //循环结束,两点位于同一重链上,但两者不一定为同一点,所以还要加上这两点之间的贡献
- if(id[x] <= id[y])
- {
- //prllf("%d %d\n", id[x], id[y]);
- ans = (ans + st.query(, , n, , id[x], id[y])) % mod;
- }
- else
- {
- //prllf("%d %d\n", id[y], id[x]);
- ans = (ans + st.query(, , n, , id[y], id[x])) % mod;
- }
- return ans;
- }
- void update_add(int x, int y, int add)
- {
- int fx = top[x], fy = top[y];
- while(fx != fy) //当两者不在同一条重链上
- {
- if(deep[fx] >= deep[fy])
- {
- st.update(, , n, id[fx], id[x], add);
- x = f[fx]; fx = top[x];
- }
- else
- {
- st.update(, , n, id[fy], id[y], add);
- y = f[fy]; fy = top[y];
- }
- }
- //循环结束,两点位于同一重链上,但两者不一定为同一点,所以还要加上这两点之间的贡献
- if(id[x] <= id[y]) st.update(, , n, id[x], id[y], add);
- else st.update(, , n, id[y], id[x], add);
- }
- int main()
- {
- scanf("%d%d%d%d", &n, &q, &root, &mod);
- for(int i = ;i <= n;i++)
- {
- scanf("%d", &w[i]);
- w[i] %= mod;
- }
- for(int i = ;i < n;i++)
- {
- int u, v;
- scanf("%d%d", &u, &v);
- addedge(u, v);
- addedge(v, u);
- }
- dfs1(root, -, );
- dfs2(root, root);
- // for(ll i = 1;i <= n;i++) prllf("%d ", id[i]);
- // prllf("\n");
- // for(ll i = 1;i <= n;i++) prllf("%d ", rk[i]);
- // prllf("\n");
- st.build(, , n);
- //scanf("%d", &q);
- while(q--)
- {
- int op;
- scanf("%d", &op);
- if(op == )
- {
- int u, v, add;
- scanf("%d%d%d", &u, &v, &add);
- update_add(u, v, add);
- }
- else if(op == )
- {
- int u, v;
- scanf("%d%d", &u, &v);
- printf("%lld\n", querysum(u, v));
- }
- else if(op == )
- {
- int u, add;
- scanf("%d%d", &u, &add);
- st.update(, , n, id[u], id[u]+size[u]-, add);
- }
- else
- {
- int u;
- scanf("%d", &u);
- printf("%lld\n",st.query(, , n, , id[u], id[u]+size[u]-));
- }
- //st.prll_debug(1, 1, n);
- }
- }
P3384——树链剖分&&模板的更多相关文章
- 洛谷 P3384 树链剖分(模板题)
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...
- BZOJ 2243 染色 | 树链剖分模板题进阶版
BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...
- 算法复习——树链剖分模板(bzoj1036)
题目: 题目背景 ZJOI2008 DAY1 T4 题目描述 一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w .我们将以下面的形式来要求你对这棵树完成一些操作:I.CHAN ...
- Hdu 5274 Dylans loves tree (树链剖分模板)
Hdu 5274 Dylans loves tree (树链剖分模板) 题目传送门 #include <queue> #include <cmath> #include < ...
- 【Luogu P3384】树链剖分模板
树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就 ...
- bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题
[ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u ...
- 洛谷P3384 树链剖分
如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...
- BZOJ 1036 树的统计Count 树链剖分模板题
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1036 题目大意: 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将 ...
- BZOJ 1036 [ZJOI2008]树的统计Count | 树链剖分模板
原题链接 树链剖分的模板题:在点带权树树上维护路径和,最大值和单点修改 这里给出几个定义 以任意点为根,然后记 size (u ) 为以 u 为根的子树的结点个数,令 v 为 u 所有儿子中 size ...
随机推荐
- 记录下为了玩 docker 安装 CentOS 7 最简化版后遇到的一些问题
今天我的腾讯云服务器在使用docker安装ElasticSearch和Kibana的时候内存不够,安装完直接卡死.所以无奈只能在本地上跑VMWare安装CentOS7来运行测试. 从阿里云镜像地址:h ...
- [转帖]localectl
localectl http://linux.51yip.com/search/localectl localectl 命令简介. 相关命令:暂无相关命令 localectl — 控制系统的本地化与键 ...
- IPv4
1.IPv4分类地址 PC0(192.168.0.1) 和PC1(172.168.0.1)如果要ping通的话需要设置各自网关 PC0 设置IP 和 默认网关=路由器设置IP 2.Gigabit ...
- 【深入浅出-JVM】(9): 方法区
概念 方法区是虚拟机规范定义的,是所有线程共享的内存区域,保存系统的类的信息.比如:类的字段.方法.常量池.构造函数的字节码内容.代码.JIT 代码 永久代.metaspace 是对方法区的实现. H ...
- 剑指offer35:数组中的逆序对
1 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%10 ...
- Python第三方库资源
[转载]Python第三方库资源 转自:https://weibo.com/ttarticle/p/show?id=2309404129469920071093 参考:https://github ...
- Python多线程VS多进程
- 【转】Entity Framework 6 Code First 实践系列(1):实体类配置-根据依赖配置关系和关联
本文转自:http://www.cnblogs.com/easygame/p/3622893.html EF实体类的配置可以使用数据注释或Fluent API两种方式配置,Fluent API配置的关 ...
- 怎样设置HTTP请求头Header
使用: xhr.setRequestHeader(); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequest ...
- Zookeeper 入门详解
zookeeper zookeeper是什么 Apache ZooKeeper是Apache软件基金会的一个软件项目,他为大型分布式计算提供开源的分布式配置服务.同步服务和命名注册.ZooKeeper ...