(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. viewpage启动页

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com ...

  2. scrapy和scrapy-redis 详解一 入门demo及内容解析

    架构及简介 Scrapy是用纯Python实现一个为了爬取网站数据.提取结构性数据而编写的应用框架,用途非常广泛. Scrapy 使用了 Twisted(其主要对手是Tornado)异步网络框架来处理 ...

  3. Redis可以做哪些事?

    Redis是一种基于键值对的NoSQL数据库,它的值主要由string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)五种基本数据结构构成,除此之外还支持一些其他的 ...

  4. 【总结】vertica数据库

    1.简介 Vertica是一款基于列式存储架构的数据库,可以支持存放多至PB级别的结构化数据 2.列式存储 行式存储就是以行为单位进行存储,再配合B+树作为索引,就能快速通过主键找到相应的行数据.即大 ...

  5. Scipy 学习第3篇:数字向量的距离计算

    计算两个数字向量u和v之间的距离函数 1,欧氏距离(Euclidean distance) 在数学中,欧几里得距离或欧几里得度量是欧几里得空间中两点间"普通"(即直线)距离.使用这 ...

  6. ETCD核心机制解析

    ETCD整体机制 etcd 是一个分布式的.可靠的 key-value 存储系统,它适用于存储分布式系统中的关键数据. etcd 集群中多个节点之间通过Raft算法完成分布式一致性协同,算法会选举出一 ...

  7. DP百题练(一)

    目录 DP百题练(一) 线性 DP 简述 Arithmetic Progressions [ZJOI2006]物流运输 LG1095 守望者的逃离 LG1103 书本整理 CH5102 移动服务 LG ...

  8. JDK新特性-Lambda表达式的神操作

    一.Lambda表达式的介绍 Lambda表达式是 Java8 中最重要的新功能之一.使用 Lambda 表达 式可以替代只有一个抽象函数的接口实现,告别匿名内部类,代码看 起来更简洁易懂.Lambd ...

  9. leetcode144 longest-palindromic-substring

    题目描述 找出给出的字符串S中最长的回文子串.假设S的最大长度为1000,并且只存在唯一解. Given a string S, find the longest palindromic substr ...

  10. 3.2spring源码系列----循环依赖源码分析

    首先,我们在3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖 中手写了循环依赖的实现. 这个实现就是模拟的spring的循环依赖. 目的是为了更容易理解spring源码 ...