题目描述

链接

如题,已知一棵包含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$标记时是不用下推的,只需查询时累加即可(不知道为什么那些题解都用下推的)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3.  
  4. typedef long long ll;
  5. #define lc o <<1
  6. #define rc o <<1 | 1
  7. //const ll INF = 0x3f3f3f3f;
  8. const int maxn = + ;
  9. struct Edge
  10. {
  11. int to, next;
  12. }edges[*maxn];
  13. int head[maxn];
  14. int cur, f[maxn], deep[maxn], size[maxn], son[maxn], rk[maxn], id[maxn], top[maxn], cnt;
  15. int n, q, w[maxn], root, mod;
  16.  
  17. inline void addedge(int u, int v)
  18. {
  19. ++cur;
  20. edges[cur].next = head[u];
  21. head[u] = cur;
  22. edges[cur].to = v;
  23. }
  24.  
  25. struct SegTree{
  26. ll sum[maxn << ], addv[maxn << ];
  27. void build(int o, int l, int r)
  28. {
  29. if(l == r)
  30. {
  31. sum[o] = w[rk[l]] % mod;
  32. }
  33. else
  34. {
  35. int mid = (l + r) >> ;
  36. build(lc, l, mid);
  37. build(rc, mid+, r);
  38. sum[o] = (sum[lc] + sum[rc]) % mod;
  39. }
  40. }
  41.  
  42. void maintain(int o, int l, int r)
  43. {
  44. if(l == r) //如果是叶子结点
  45. sum[o] = w[rk[l]] % mod;
  46. else //如果是非叶子结点
  47. sum[o] = (sum[lc] + sum[rc]) % mod;
  48.  
  49. sum[o] = (sum[o] + addv[o] * (r-l+)) % mod;
  50. }
  51. //区间修改,[cl,cr] += v;
  52. void update(int o, int l, int r, int cl, int cr, int v) //
  53. {
  54. if(cl <= l && r <= cr) addv[o] = (addv[o] + v) % mod;
  55. else
  56. {
  57. int m = l + (r-l) /;
  58. if(cl <= m) update(lc, l, m, cl, cr, v);
  59. if(cr > m) update(rc, m+, r, cl, cr, v);
  60. }
  61. maintain(o, l, r);
  62. }
  63.  
  64. //区间查询,sum{ql,qr}
  65. ll query(int o, int l,int r, ll add, int ql, int qr)
  66. {
  67. if(ql <= l && r <= qr)
  68. {
  69. //prllf("sum[o]:%d %d*(%d-%d+1)\n", sum[o], add, r, l);
  70. return (sum[o] + add * (r-l+)) % mod; //tx l-r+1
  71. }
  72. else
  73. {
  74. int m = l + (r - l) / ;
  75. ll ans = ;
  76. add = (add + addv[o]) % mod;
  77. if(ql <= m) ans = (ans + query(lc, l, m, add, ql, qr)) % mod;
  78. if(qr > m) ans = (ans + query(rc, m+, r, add, ql, qr)) % mod;
  79. return ans;
  80. }
  81. }
  82. }st;
  83.  
  84. void dfs1(int u, int fa, int depth) //当前节点、父节点、层次深度
  85. {
  86. //prllf("u:%d fa:%d depth:%d\n", u, fa, depth);
  87. f[u] = fa;
  88. deep[u] = depth;
  89. size[u] = ; //这个点本身的size
  90. for(int i = head[u];i;i = edges[i].next)
  91. {
  92. int v = edges[i].to;
  93. if(v == fa) continue;
  94. dfs1(v, u, depth+);
  95. size[u] += size[v]; //子节点的size已被处理,用它来更新父节点的size
  96. if(size[v] > size[son[u]]) son[u] = v; //选取size最大的作为重儿子
  97. }
  98. }
  99.  
  100. void dfs2(int u, int t) //当前节点、重链顶端
  101. {
  102. //prllf("u:%d t:%d\n", u, t);
  103. top[u] = t;
  104. id[u] = ++cnt; //标记dfs序
  105. rk[cnt] = u; //序号cnt对应节点u
  106. if(!son[u]) return; //没有儿子?
  107. dfs2(son[u], t); //我们选择优先进入重儿子来保证一条重链上各个节点dfs序连续
  108.  
  109. for(int i = head[u];i;i = edges[i].next)
  110. {
  111. int v = edges[i].to;
  112. if(v != son[u] && v != f[u]) dfs2(v, v); //这个点位于轻链顶端,那么它的top必然为它本身
  113. }
  114. }
  115.  
  116. /*修改和查询的原理是一致的,以查询操作为例,其实就是个LCA,不过这里要使用top数组加速,因为top可以直接跳到该重链的起始顶点*/
  117. /*注意,每次循环只能跳一次,并且让结点深的那个跳到top的位置,避免两者一起跳而插肩而过*/
  118. ll querysum(int x, int y)
  119. {
  120. int fx = top[x], fy = top[y];
  121. ll ans = ;
  122. while(fx != fy) //当两者不在同一条重链上
  123. {
  124. if(deep[fx] >= deep[fy])
  125. {
  126. //prllf("%d %d\n", id[fx], id[x]);
  127. ans = (ans + st.query(, , n, , id[fx], id[x])) % mod; //线段树区间求和,计算这条重链的贡献
  128. x = f[fx]; fx = top[x];
  129. }
  130. else
  131. {
  132. //prllf("%d %d\n", id[fy], id[y]);
  133. ans = (ans + st.query(, , n, , id[fy], id[y])) % mod;
  134. y = f[fy]; fy = top[y];
  135. }
  136. }
  137.  
  138. //循环结束,两点位于同一重链上,但两者不一定为同一点,所以还要加上这两点之间的贡献
  139. if(id[x] <= id[y])
  140. {
  141. //prllf("%d %d\n", id[x], id[y]);
  142. ans = (ans + st.query(, , n, , id[x], id[y])) % mod;
  143. }
  144. else
  145. {
  146. //prllf("%d %d\n", id[y], id[x]);
  147. ans = (ans + st.query(, , n, , id[y], id[x])) % mod;
  148. }
  149. return ans;
  150. }
  151.  
  152. void update_add(int x, int y, int add)
  153. {
  154. int fx = top[x], fy = top[y];
  155. while(fx != fy) //当两者不在同一条重链上
  156. {
  157. if(deep[fx] >= deep[fy])
  158. {
  159. st.update(, , n, id[fx], id[x], add);
  160. x = f[fx]; fx = top[x];
  161. }
  162. else
  163. {
  164. st.update(, , n, id[fy], id[y], add);
  165. y = f[fy]; fy = top[y];
  166. }
  167. }
  168. //循环结束,两点位于同一重链上,但两者不一定为同一点,所以还要加上这两点之间的贡献
  169. if(id[x] <= id[y]) st.update(, , n, id[x], id[y], add);
  170. else st.update(, , n, id[y], id[x], add);
  171. }
  172.  
  173. int main()
  174. {
  175. scanf("%d%d%d%d", &n, &q, &root, &mod);
  176. for(int i = ;i <= n;i++)
  177. {
  178. scanf("%d", &w[i]);
  179. w[i] %= mod;
  180. }
  181. for(int i = ;i < n;i++)
  182. {
  183. int u, v;
  184. scanf("%d%d", &u, &v);
  185. addedge(u, v);
  186. addedge(v, u);
  187. }
  188. dfs1(root, -, );
  189. dfs2(root, root);
  190.  
  191. // for(ll i = 1;i <= n;i++) prllf("%d ", id[i]);
  192. // prllf("\n");
  193. // for(ll i = 1;i <= n;i++) prllf("%d ", rk[i]);
  194. // prllf("\n");
  195.  
  196. st.build(, , n);
  197. //scanf("%d", &q);
  198. while(q--)
  199. {
  200. int op;
  201. scanf("%d", &op);
  202. if(op == )
  203. {
  204. int u, v, add;
  205. scanf("%d%d%d", &u, &v, &add);
  206. update_add(u, v, add);
  207. }
  208. else if(op == )
  209. {
  210. int u, v;
  211. scanf("%d%d", &u, &v);
  212. printf("%lld\n", querysum(u, v));
  213. }
  214. else if(op == )
  215. {
  216. int u, add;
  217. scanf("%d%d", &u, &add);
  218. st.update(, , n, id[u], id[u]+size[u]-, add);
  219. }
  220. else
  221. {
  222. int u;
  223. scanf("%d", &u);
  224. printf("%lld\n",st.query(, , n, , id[u], id[u]+size[u]-));
  225. }
  226. //st.prll_debug(1, 1, n);
  227. }
  228. }

P3384——树链剖分&&模板的更多相关文章

  1. 洛谷 P3384 树链剖分(模板题)

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

  2. BZOJ 2243 染色 | 树链剖分模板题进阶版

    BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...

  3. 算法复习——树链剖分模板(bzoj1036)

    题目: 题目背景 ZJOI2008 DAY1 T4 题目描述 一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w .我们将以下面的形式来要求你对这棵树完成一些操作:I.CHAN ...

  4. Hdu 5274 Dylans loves tree (树链剖分模板)

    Hdu 5274 Dylans loves tree (树链剖分模板) 题目传送门 #include <queue> #include <cmath> #include < ...

  5. 【Luogu P3384】树链剖分模板

    树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就 ...

  6. bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题

    [ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u ...

  7. 洛谷P3384 树链剖分

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...

  8. BZOJ 1036 树的统计Count 树链剖分模板题

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1036 题目大意: 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将 ...

  9. BZOJ 1036 [ZJOI2008]树的统计Count | 树链剖分模板

    原题链接 树链剖分的模板题:在点带权树树上维护路径和,最大值和单点修改 这里给出几个定义 以任意点为根,然后记 size (u ) 为以 u 为根的子树的结点个数,令 v 为 u 所有儿子中 size ...

随机推荐

  1. 记录下为了玩 docker 安装 CentOS 7 最简化版后遇到的一些问题

    今天我的腾讯云服务器在使用docker安装ElasticSearch和Kibana的时候内存不够,安装完直接卡死.所以无奈只能在本地上跑VMWare安装CentOS7来运行测试. 从阿里云镜像地址:h ...

  2. [转帖]localectl

    localectl http://linux.51yip.com/search/localectl localectl 命令简介. 相关命令:暂无相关命令 localectl — 控制系统的本地化与键 ...

  3. IPv4

    1.IPv4分类地址 PC0(192.168.0.1) 和PC1(172.168.0.1)如果要ping通的话需要设置各自网关 PC0  设置IP  和  默认网关=路由器设置IP 2.Gigabit ...

  4. 【深入浅出-JVM】(9): 方法区

    概念 方法区是虚拟机规范定义的,是所有线程共享的内存区域,保存系统的类的信息.比如:类的字段.方法.常量池.构造函数的字节码内容.代码.JIT 代码 永久代.metaspace 是对方法区的实现. H ...

  5. 剑指offer35:数组中的逆序对

    1 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%10 ...

  6. Python第三方库资源

    [转载]Python第三方库资源   转自:https://weibo.com/ttarticle/p/show?id=2309404129469920071093 参考:https://github ...

  7. Python多线程VS多进程

  8. 【转】Entity Framework 6 Code First 实践系列(1):实体类配置-根据依赖配置关系和关联

    本文转自:http://www.cnblogs.com/easygame/p/3622893.html EF实体类的配置可以使用数据注释或Fluent API两种方式配置,Fluent API配置的关 ...

  9. 怎样设置HTTP请求头Header

    使用: xhr.setRequestHeader(); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequest ...

  10. Zookeeper 入门详解

    zookeeper zookeeper是什么 Apache ZooKeeper是Apache软件基金会的一个软件项目,他为大型分布式计算提供开源的分布式配置服务.同步服务和命名注册.ZooKeeper ...