题目大意

已知一棵包含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为根节点的子树内所有节点值之和

基本概念

对路径和子树上的值进行整体操作,我们要用线段树。每个节点有个Id,对应线段树维护的区间(以后简称区间)上的点。Id要满足以下条件:

  1. 所有子树上的节点的Id构成一段连续的区间。
  2. 每个节点都属于且只属于一个重链,使得重链上的节点的Id构成一段连续的区间。

对于一个节点u,它的子节点v属于u所在重链当且仅当v是u的孩子中size(子树中元素的个数)最大的。此时v叫做u的重孩子。

连接两个属于不同重链的节点的是轻边,这两个节点的Id之差必大于1。

实现方法

预处理

先Dfs1求出每个节点的Size,深度,并通过Size求出每个节点的重儿子;再Dfs2求出重链(具体表示为每个节点所在链的链头),并按照Dfs序设置每个节点的Id和LastSonId(Dfs到的子树中的最后一个节点的Id)。

子树操作

直接用线段树操作当前节点的Id和LastSonId即可。

路径操作

当两个节点u,v所在重链的链头不同时,令u为所在链头深度较深的节点,用线段树对u的链头的Id和u的Id进行操作,然后u通过与u链头相连的轻边移动到u链头的父亲那里去,如此循环。最后,当u,v所在链头相同时,令u为深度较深的节点,用线段树对v的Id和u的Id操作即可。

注意事项

  • 边的数量应当是节点数量的二倍,因为u到v一条边,v到u也一条边。
  • 令u为所在链头深度较深的节点,而不是令u为深度较深的节点。
  • 路径操作对uv链头不同时的循环完后,即使u==v,也要线段树操作。
  • 由于题目中要求取模,所以struct SplitTree中没有一个+号。
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cassert>
  4. #include <algorithm>
  5. using namespace std;
  6.  
  7. const int MAX_NODE = 100010, MAX_EDGE = MAX_NODE*2, MAX_RANGE_NODE = MAX_NODE * 10;
  8. int P;
  9. #define LOOP(i, n) for(int i=1; i<=n; i++)
  10.  
  11. struct SplitTree
  12. {
  13. private:
  14. #define ModPlus(x, y) ((x)+((y)%P))%P
  15.  
  16. struct Node;
  17. struct Edge;
  18.  
  19. struct Node
  20. {
  21. Node *HeavySon, *Top, *Father;
  22. Edge *Head;
  23. int Size, Id, LastSonId, Weight, Depth;
  24. }_nodes[MAX_NODE], *Root;
  25.  
  26. struct Edge
  27. {
  28. Edge *Next;
  29. Node *From, *To;
  30. }*_edges[MAX_EDGE];
  31.  
  32. int _lastId, _edgeCnt;
  33.  
  34. struct RangeTree
  35. {
  36. private:
  37. int Sum[MAX_RANGE_NODE], PlusTag[MAX_RANGE_NODE];
  38. int TotRange;
  39.  
  40. void PushDown(int cur, int sl, int sr)
  41. {
  42. if (PlusTag[cur])
  43. {
  44. int mid = (sl + sr) / 2;
  45. PlusTag[cur * 2] = ModPlus(PlusTag[cur * 2], PlusTag[cur]);
  46. PlusTag[cur * 2 + 1] = ModPlus(PlusTag[cur * 2 + 1], PlusTag[cur]);
  47. Sum[cur * 2] = ModPlus(Sum[cur * 2], PlusTag[cur] * (mid - sl + 1));
  48. Sum[cur * 2 + 1] = ModPlus(Sum[cur * 2 + 1], PlusTag[cur] * (sr - mid));
  49. PlusTag[cur] = 0;
  50. }
  51. }
  52.  
  53. void PullUp(int cur)
  54. {
  55. Sum[cur] = ModPlus(Sum[cur * 2], Sum[cur * 2 + 1]);
  56. }
  57.  
  58. void Update(int cur, int sl, int sr, int al, int ar, int value)
  59. {
  60. if (al <= sl && sr <= ar)
  61. {
  62. Sum[cur] = ModPlus(Sum[cur], (sr - sl + 1)*value);
  63. PlusTag[cur] = ModPlus(PlusTag[cur], value);
  64. return;
  65. }
  66. PushDown(cur, sl, sr);
  67. int mid = (sl + sr) / 2;
  68. if (al <= mid)
  69. Update(cur * 2, sl, mid, al, ar, value);
  70. if (ar > mid)
  71. Update(cur * 2 + 1, mid + 1, sr, al, ar, value);
  72. PullUp(cur);
  73. }
  74.  
  75. int Query(int cur, int sl, int sr, int al, int ar)
  76. {
  77. if (al <= sl&&sr <= ar)
  78. return Sum[cur];
  79. PushDown(cur, sl, sr);
  80. int mid = (sl + sr) / 2, ans = 0;
  81. if (al <= mid)
  82. ans = ModPlus(ans, Query(cur * 2, sl, mid, al, ar));
  83. if (ar > mid)
  84. ans = ModPlus(ans, Query(cur * 2 + 1, mid + 1, sr, al, ar));
  85. PullUp(cur);
  86. return ans;
  87. }
  88.  
  89. public:
  90. void Update(int l, int r, int value)
  91. {
  92. Update(1, 1, TotRange, l, r, value);
  93. }
  94.  
  95. int Query(int l, int r)
  96. {
  97. return Query(1, 1, TotRange, l, r);
  98. }
  99.  
  100. void Init(int totRange)
  101. {
  102. memset(Sum, 0, sizeof(Sum));
  103. memset(PlusTag, 0, sizeof(PlusTag));
  104. TotRange = totRange;
  105. }
  106. }r;
  107.  
  108. Edge *NewEdge()
  109. {
  110. return _edges[++_edgeCnt] = new Edge();
  111. }
  112.  
  113. void AddEdge(Node *from, Node *to)
  114. {
  115. Edge *e = NewEdge();
  116. e->From = from;
  117. e->To = to;
  118. e->Next = e->From->Head;
  119. e->From->Head = e;
  120. }
  121.  
  122. void Dfs1(Node *cur, Node *father, int depth)
  123. {
  124. cur->Size = 1;
  125. cur->Depth = depth;
  126. cur->Father = father;
  127. int maxSonSize = 0;
  128. for (Edge *e = cur->Head; e; e = e->Next)
  129. {
  130. if (e->To != father)
  131. {
  132. Dfs1(e->To, cur, depth + 1);
  133. cur->Size += e->To->Size;
  134. if (e->To->Size > maxSonSize)
  135. {
  136. maxSonSize = e->To->Size;
  137. cur->HeavySon = e->To;
  138. }
  139. }
  140. }
  141. }
  142.  
  143. void Dfs2(Node *cur, Node *top)
  144. {
  145. cur->Top = top;
  146. cur->Id = ++_lastId;
  147. r.Update(cur->Id, cur->Id, cur->Weight);
  148. if (cur->HeavySon)
  149. Dfs2(cur->HeavySon, top);
  150. for (Edge *e = cur->Head; e; e = e->Next)
  151. if (e->To != cur->HeavySon && e->To != cur->Father)
  152. Dfs2(e->To, e->To);
  153. cur->LastSonId = _lastId;
  154. }
  155.  
  156. void UpdatePath(Node *u, Node *v, int value)
  157. {
  158. while (u->Top != v->Top)
  159. {
  160. if (u->Top->Depth < v->Top->Depth)
  161. swap(u, v);
  162. r.Update(u->Top->Id, u->Id, value);
  163. u = u->Top->Father;
  164. }
  165. if (u->Depth < v->Depth)
  166. swap(u, v);
  167. r.Update(v->Id, u->Id, value);
  168. }
  169.  
  170. int QueryPath(Node *u, Node *v)
  171. {
  172. int sum = 0;
  173. while (u->Top != v->Top)
  174. {
  175. if (u->Top->Depth < v->Top->Depth)
  176. swap(u, v);
  177. sum = ModPlus(sum, r.Query(u->Top->Id, u->Id));
  178. u = u->Top->Father;
  179. }
  180. if (u->Depth < v->Depth)
  181. swap(u, v);
  182. sum = ModPlus(sum, r.Query(v->Id, u->Id));
  183. return sum;
  184. }
  185.  
  186. void UpdateSubTree(Node *cur, int value)
  187. {
  188. r.Update(cur->Id, cur->LastSonId, value);
  189. }
  190.  
  191. int QuerySubTree(Node *cur)
  192. {
  193. return r.Query(cur->Id, cur->LastSonId);
  194. }
  195.  
  196. public:
  197. SplitTree(int root, int totNode)
  198. {
  199. memset(_nodes, 0, sizeof(_nodes));
  200. memset(_edges, 0, sizeof(_edges));
  201. _lastId = _edgeCnt = 0;
  202. Root = _nodes + root;
  203. r.Init(totNode);
  204. }
  205.  
  206. void SetNodeWeight(int id, int w)
  207. {
  208. _nodes[id].Weight = w;
  209. }
  210.  
  211. void Build(int u, int v)
  212. {
  213. AddEdge(_nodes + u, _nodes + v);
  214. AddEdge(_nodes + v, _nodes + u);
  215. }
  216.  
  217. void Init()
  218. {
  219. Dfs1(Root, NULL, 1);
  220. Dfs2(Root, Root);
  221. }
  222.  
  223. void UpdatePath(int u, int v, int value)
  224. {
  225. UpdatePath(_nodes + u, _nodes + v, value);
  226. }
  227.  
  228. int QueryPath(int u, int v)
  229. {
  230. return QueryPath(_nodes + u, _nodes + v);
  231. }
  232.  
  233. void UpdateSubTree(int u, int value)
  234. {
  235. UpdateSubTree(_nodes + u, value);
  236. }
  237.  
  238. int QuerySubTree(int u)
  239. {
  240. return QuerySubTree(_nodes + u);
  241. }
  242. };
  243.  
  244. int main()
  245. {
  246. int totNode, rootId, opCnt, w, u, v, op, val;
  247. scanf("%d%d%d%d", &totNode, &opCnt, &rootId, &P);
  248. static SplitTree g(rootId, totNode);
  249. LOOP(i, totNode)
  250. {
  251. scanf("%d", &w);
  252. g.SetNodeWeight(i, w);
  253. }
  254. LOOP(i, totNode - 1)
  255. {
  256. scanf("%d%d", &u, &v);
  257. g.Build(u, v);
  258. }
  259. g.Init();
  260. while (opCnt--)
  261. {
  262. scanf("%d", &op);
  263. switch (op)
  264. {
  265. case 1://UpdatePath
  266. scanf("%d%d%d", &u, &v, &val);
  267. g.UpdatePath(u, v, val);
  268. break;
  269. case 2://QueryPath
  270. scanf("%d%d", &u, &v);
  271. printf("%d\n", g.QueryPath(u, v));
  272. break;
  273. case 3://UpdateSubTree
  274. scanf("%d%d", &u, &val);
  275. g.UpdateSubTree(u, val);
  276. break;
  277. case 4://QuerySubTree
  278. scanf("%d", &u);
  279. printf("%d\n", g.QuerySubTree(u));
  280. //printf("100\n");
  281. break;
  282. }
  283. }
  284. return 0;
  285. }

  

luogu3384 【模板】 树链剖分的更多相关文章

  1. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  2. luoguP3384 [模板]树链剖分

    luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...

  3. [洛谷P3384] [模板] 树链剖分

    题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...

  4. 模板 树链剖分BFS版本

    //点和线段树都从1开始 //边使用vector vector<int> G[maxn]; ],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn], ...

  5. P3384 [模板] 树链剖分

    #include <bits/stdc++.h> using namespace std; typedef long long ll; int n, m, rt, mod, cnt, to ...

  6. 树链剖分详解(洛谷模板 P3384)

    洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 emm还有必备的 链式前向星.线段树 也要先学了. 如果这三个知识点没掌握好的话,树链剖 ...

  7. 『题解』洛谷P3384 【模板】树链剖分

    Problem Portal Portal1: Luogu Description 如题,已知一棵包含\(N\)个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作\(1\): ...

  8. luogu3384 【模板】树链剖分

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

  9. luogu3384 /// 树链剖分+线段树模板

    题目大意: https://www.luogu.org/problemnew/show/P3384 树链剖分的讲解 两个dfs() 修改 查询 很详细很好理解 https://www.cnblogs. ...

  10. P3384 【模板】树链剖分

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

随机推荐

  1. 【Luogu】P2894酒店Hotel(线段树)

    题目链接 我好蒻啊   题题看题解 线段树维护从左端点开始的最长连续空房.右端点结束的最长连续空房.整段区间的最长连续空房.区间非空房的个数. http://blog.csdn.net/qq_3955 ...

  2. HDU——2083找单词(母函数)

    找单词 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submissio ...

  3. Spring Cloud 从入门到精通

    Spring Cloud 是一套完整的微服务解决方案,基于 Spring Boot 框架,准确的说,它不是一个框架,而是一个大的容器,它将市面上较好的微服务框架集成进来,从而简化了开发者的代码量. 本 ...

  4. CentOS7下安装Docker-Compose No module named 'requests.packages.urllib3'

    在使用Docker的时候,有一个工具叫做  docker-compose,安装它的前提是要安装pip工具. 1.首先检查Linux有没有安装Python-pip包,直接执行 yum install p ...

  5. Codeforces 894.A QAQ

    A. QAQ time limit per test 1 second memory limit per test 256 megabytes input standard input output ...

  6. servlet对form提交的数据进行XML转换后发送

    今天遇到一个项目,要求对form表单提交的数据进行以xml格式发送出去: 直接写XMLUtil工具类如下: package com.yfit.controller; import javax.serv ...

  7. spring和mybatis整合配置文件

    查看所有springmvc  spring  mybatis配置文件见下链接: https://my.oschina.net/sherwayne/blog/262616 <?xml versio ...

  8. 【Vim命令大全】史上最全的Vim命令

    曾经使用了两年多的Vim,手册也翻过一遍.虽然现在不怎么用vim了,曾经的笔记还是贴出来,与喜欢vim的朋友分享. 1. 关于Vim vim是我最喜欢的编辑器,也是linux下第二强大的编辑器. 虽然 ...

  9. POJ Blue Jeans [枚举+KMP]

    传送门 F - Blue Jeans Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u ...

  10. Xcode打包应用为ipa

    Xcode教程 Xcode4发布测试 打包Archive操作是本文要介绍的内容,发布测试的最后一步打包(Archive),Xcode4帮助文档有比较详细介绍,但是居然是错的,这里说明一下. 1.设置& ...