(7.16晚)更完先在B站颓一会儿……

---------------------------------------------------------------

(以下为luogu题面)

题目描述

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。 有q次询问,每次询问给出l r z,求∑(l≤i≤r ​dep[LCA(i,z)])

输入输出格式

输入格式:

第一行2个整数n q。 接下来n-1行,分别表示点1到点n-1的父节点编号。 接下来q行,每行3个整数l r z。

输出格式:

输出q行,每行表示一个询问的答案。每个答案对201314取模输出

说明

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

  需要说明的是,这道题的思路跟LCA的三种求法无关(树剖太暴力了,而且确实会用到树剖),也就是说没有丝毫的暴力分可拿。(掀桌)

  从这道题中对于深度的定义可以发现,两个点LCA的深度等价于它们的公共祖先的数目。那么问题的本质是,每次给定一个点集,求集合中每个点与一个定点的祖先数目的累计。考虑这样的暴力处理:对于任取一个点u与所给定点z,我们可以先给u的每个祖先都打一层标记,然后询问从z到根有标记的点的数目。这个过程显然可以用树剖处理,我们在树剖序上架一个线段树支持区间修改、区间查询,每个这样的询问可以做到log^2n的复杂度。

  如果我们对每次询问,都把点集中的每个点这样打一遍祖先的标记再查询z,总复杂度会多出一个q来,显然需要进一步优化。题中给出的点集有一个非常好的性质:点集中的点处于一段连续区间中。那么,我们可以把每个询问[l, r]差分为[1, l - 1]和[1, r]两个询问,那么需要维护的就只有形如[1, R]与每个z的关系了。

  因此我们把询问离线差分后按右端点排序,从左向右扫描点集[1, n]并打标记,每遇到一个询问则查询给定z与根之间的标记数目,统计进询问数组即可。

代码:

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <cstring>
  4. #include <algorithm>
  5. #define lowbit(i) (i & -i)
  6. #define maxn 50010
  7. #define mod 201314
  8. template <typename T>
  9. void read(T &x) {
  10. x = 0;
  11. int f = 1;
  12. char ch = getchar();
  13. while (!isdigit(ch)) {
  14. if (ch == '-')
  15. f = -1;
  16. ch = getchar();
  17. }
  18. while (isdigit(ch)) {
  19. x = x * 10 + (ch ^ 48);
  20. ch = getchar();
  21. }
  22. x *= f;
  23. return;
  24. }
  25. using namespace std;
  26. int head[maxn], top;
  27. struct E {
  28. int to, nxt;
  29. } edge[maxn];
  30. inline void insert(int u, int v) {
  31. edge[++top] = (E) {v, head[u]};
  32. head[u] = top;
  33. }
  34. int n, q, ans[maxn], qtp;
  35. struct Q {
  36. int r, z, id;
  37. bool op;
  38. friend bool operator < (Q a, Q b) {
  39. return a.r < b.r;
  40. }
  41. } ask[maxn << 1];
  42. /*namespace BIT {
  43. int bit[maxn];
  44. void modify(int x, int val) {
  45. for (int i = x; i <= n; i += lowbit(i))
  46. bit[i] += val;
  47. }
  48. int presum(int x) {
  49. int sum = 0;
  50. for (int i = x; i; i -= lowbit(i))
  51. sum = (sum + bit[i]) % mod;
  52. return sum;
  53. }
  54. }*/
  55. namespace Segment_tree {
  56. #define lc (nd<<1)
  57. #define rc ((nd<<1)|1)
  58. #define mid ((l+r)>>1)
  59. struct node {
  60. int val, len;
  61. friend node operator + (node a, node b) {
  62. return (node) {(a.val + b.val) % mod, a.len + b.len};
  63. }
  64. } seg[maxn << 2];
  65. int tag[maxn << 2];
  66. inline void update(int nd) {
  67. seg[nd] = seg[lc] + seg[rc];
  68. }
  69. inline void put_tag(int nd, int op) {
  70. seg[nd].val += op * seg[nd].len;
  71. tag[nd] += op;
  72. }
  73. inline void push_down(int nd) {
  74. put_tag(lc, tag[nd]);
  75. put_tag(rc, tag[nd]);
  76. tag[nd] = 0;
  77. }
  78. void build(int nd, int l, int r) {
  79. if (l == r) {
  80. seg[nd] = (node) {0, 1};
  81. return;
  82. }
  83. build(lc, l, mid);
  84. build(rc, mid + 1, r);
  85. update(nd);
  86. }
  87. void modify(int nd, int l, int r, int ql, int qr, int val) {
  88. if (l >= ql && r <= qr) {
  89. put_tag(nd, val);
  90. return;
  91. } else if (l > qr || r < ql)
  92. return;
  93. push_down(nd);
  94. modify(lc, l, mid, ql, qr, val);
  95. modify(rc, mid + 1, r, ql, qr, val);
  96. update(nd);
  97. return;
  98. }
  99. int query(int nd, int l, int r, int ql, int qr) {
  100. if (l >= ql && r <= qr)
  101. return seg[nd].val;
  102. if (l > qr || r < ql)
  103. return 0;
  104. push_down(nd);
  105. return (query(lc, l, mid, ql, qr) + query(rc, mid + 1, r, ql, qr)) % mod;
  106. }
  107. }
  108. namespace Div_tree {  //树剖
  109. //  using namespace BIT;  //试图用树状数组维护区间修改的惨痛失败
  110. using namespace Segment_tree;
  111. int dfn[maxn], size[maxn], ftop[maxn], d[maxn], son[maxn], f[maxn];
  112. int tmr;
  113. void dfs1(int u, int pre) {
  114. f[u] = pre;
  115. d[u] = d[pre] + 1;
  116. size[u] = 1;
  117. for (int i = head[u]; i; i = edge[i].nxt) {
  118. int v = edge[i].to;
  119. dfs1(v, u);
  120. size[u] += size[v];
  121. if (size[v] > size[son[u]])
  122. son[u] = v;
  123. }
  124. }
  125. void dfs2(int u, int tp) {
  126. dfn[u] = ++tmr;
  127. ftop[u] = tp;
  128. if (!son[u])
  129. return;
  130. dfs2(son[u], tp);
  131. for (int i = head[u]; i; i = edge[i].nxt) {
  132. int v = edge[i].to;
  133. if (v != son[u])
  134. dfs2(v, v);
  135. }
  136. }
  137. void Mrange(int u, int v, int val) {
  138. while (ftop[u] != ftop[v]) {
  139. if (d[ftop[u]] < d[ftop[v]])
  140. swap(u, v);
  141. modify(1, 1, n, dfn[ftop[u]], dfn[u], val);
  142. u = f[ftop[u]];
  143. }
  144. if (d[u] < d[v]) swap(u, v);
  145. modify(1, 1, n, dfn[v], dfn[u], val);
  146. return;
  147. }
  148. int Qrange(int u, int v) {
  149. int sum = 0;
  150. while (ftop[u] != ftop[v]) {
  151. if (d[ftop[u]] < d[ftop[v]])
  152. swap(u, v);
  153. sum = (sum + query(1, 1, n, dfn[ftop[u]], dfn[u])) % mod;
  154. u = f[ftop[u]];
  155. }
  156. if (d[u] < d[v]) swap(u, v);
  157. sum += query(1, 1, n, dfn[v], dfn[u]);
  158. return sum % mod;
  159. }
  160. } using namespace Div_tree;
  161. void init() {
  162. build(1, 1, n);
  163. dfs1(1, 0);
  164. dfs2(1, 1);
  165. }
  166. int main() {
  167. read(n), read(q);
  168. int u, v, z;
  169. for (int i = 2; i <= n; ++i)//编号+1
  170. read(u), ++u, insert(u, i);
  171. for (int i = 1; i <= q; ++i) {
  172. read(u), read(v), read(z);
  173. ask[++qtp] = (Q) {u, z+1, i, 0};  //拆询问
  174. ask[++qtp] = (Q) {v+1, z+1, i, 1};
  175. }
  176. sort(ask + 1, ask + qtp + 1);
  177. init();
  178. int i = 0, j = 1;
  179. while (j <= qtp) {
  180. while (i < ask[j].r)
  181. Mrange(1, ++i, 1);
  182. if (ask[j].op)
  183. ans[ask[j].id] += Qrange(1, ask[j].z);  //按标记统计进答案
  184. else ans[ask[j].id] -= Qrange(1, ask[j].z);
  185. ++j;
  186. }
  187. for (int i = 1; i <= q; ++i)
  188. printf("%d\n", (ans[i]+mod)%mod);
  189. return 0;
  190. }

【P4211 LNOI2014】LCA——树链剖分 +询问离线的更多相关文章

  1. 洛谷 P4211 [LNOI2014]LCA (树链剖分+离线)

    题目:https://www.luogu.org/problemnew/solution/P4211 相当难的一道题,其思想难以用言语表达透彻. 对于每个查询,区间[L,R]中的每个点与z的lca肯定 ...

  2. 洛谷$P4211\ [LNOI2014]\ LCA$ 树链剖分+线段树

    正解:树剖+线段树 解题报告: 传送门$QwQ$ 看到$dep[lca]$啥的就想到之前托腮腮$CSP$模拟$D1T3$的那个套路,,, 然后试下这个想法,于是$dep[lca(x,y)]=\sum_ ...

  3. [BZOJ3626] [LNOI2014]LCA(树链剖分)

    [BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...

  4. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status ...

  5. BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )

    说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...

  6. BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线

    http://www.lydsy.com/JudgeOnline/problem.php?id=3626 LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题 ...

  7. BZOJ3626[LNOI2014]LCA——树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

  8. bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...

  9. 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

随机推荐

  1. 在嵌入式设备中实现webrtc的第三种方式①

    最近两年,我对于网络知识,包括底层协议学习比较多,webrtc这种几乎是使用到了所有层面网络协议的技术也逐渐进入我的视野. 之前我提出了两种在嵌入式设备上实现webrtc的方式,一是用官方代码,然后改 ...

  2. Django model总结(上)

    Django model是django框架中处于比较核心的一个部位,准备分三个博客从不同的方面分别进行阐述,本文为<上篇>,主要对[a]Model的基本流程,比如它的创建,迁移等:默认行为 ...

  3. 关于天线长度及LC值的计算

    一.天线长度与波长 1.天线最佳长度计算 理论和实践证明,当天线的长度为无线电信号波长的1/4时,天线的发射和接收转换效率最高.因此,天线的长度将根据所发射和接收信号的频率即波长来决定.只要知道对应发 ...

  4. 上午小测1 B.序列 哈希表+数学

    题目描述 \(EZ\) 每周一都要举行升旗仪式,国旗班会站成一整列整齐地向前行进. 郭神摄像师想要选取其中一段照下来.他想让这一段中每个人的身高成等比数列,展示出最萌身高差.但他发现这个太难办到了.于 ...

  5. 《Head First 设计模式》:迭代器模式

    正文 一.定义 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示. 要点: 迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象.这样简化了聚合的接口和实现,也让责 ...

  6. C#中的release和debug模式

    以下内容来源:https://www.cnblogs.com/rgjycs/p/9254332.html 在程序调试时的debug和release 网上有如下的描述:Debug 通常称为调试版本,它包 ...

  7. 简单入门Rabbitmq

    什么是RabbitMQ RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写.支持多种客户端,如:Python.Ruby..NET.Java.JMS.C.PHP.ActionScr ...

  8. C#设计模式-责任链模式(Chain of Responsibility Pattern)

    引子 一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现.现在以请假流程为例,一般公 ...

  9. Windows自带MD5 SHA1 SHA256命令行工具

    感恩大佬LiuYanYGZ的文章 MyHash 检验工具http://www.zdfans.com/html/4346.html HashMyFiles Hash校验工具http://www.nirs ...

  10. C++ 数据结构 4:排序

    1 基本概念 1.1 定义 排序是计算机内经常进行的一种操作,其目的是将一组"无序"的数据元素调整为"有序"的数据元素. 1.2 数学定义 假设含n个数据元素的 ...