题目描述

强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在这个树上从 i 到 j 的唯一路径上所有边的边权和。强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。  
我们假定这个树一开始为空,节点按照加入的顺序从 1开始编号。由于强强非常好奇, 你必须在他每次出现新节点后马上给出总共的朋友对数,不能拖延哦。

输入

共有 n + 2 行。 
第一行包含一个正整数,表示测试点编号。 
第二行包含一个正整数 n ,表示总共要加入的节点数。 
我们令加入节点前的总共朋友对数是 last_ans,在一开始时它的值为0。 
接下来 n 行中第 i 行有三个数 ai, bi, ri,表示节点  i  的父节点的编号为 ai xor (last_ans mod 10^9)   (其中xor 表示异或,mod  表示取余,数据保证这样操作后得到的结果介于 1到i  –  1之间),与父节点之间的边权为 ci,节点 i 上小精灵的感受能力值为r!。 
注意 a1 = c1 = 0,表示 1 号点是根节点,对于 i > 1,父节点的编号至少为1。

输出

包含 n 行,每行输出1 个整数, 表示加入第 i 个点之后,树上有几对朋友。

样例输入

0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4

样例输出

0
1
2
4
7


题解

替罪点分树套SBT

如果不考虑插入新点位置(即可以离线把树建出来),只考虑查询的话,那么就相当于查询$dis(i,j)\ge r_i+r_j$,即$dis(i,j)-r_j\ge r_i$的点$j$有多少个。

考虑点分治,一个点的子树中某子节点子树以外的节点到该子节点子树中某点的距离,可以看作先到根节点,再从根节点到这个点。所以可以使用容斥的方法,用所有的点减去在该子树内的点,维护某子树中所有点的$dis(x,rt)-r$,和某子树中所有点的$dis(x,fa[rt])-r$,在查找时,即找出$dis-r\ge r_i-dis(i,rt)$的所有点,于是使用平衡树SBT维护这两个信息,并在平衡树里查找即可。

然而本题强制在线,因此不能把整棵点分树建出来然后处理询问。

这时就可以使用替罪羊树“替罪”的思想,当距离偏差大的时候暴力重构。这样就可以维护一个近似平衡的分治结构以处理询问。

对于每个加点操作,直接把它挂到父亲节点之下,并向上更新子树信息。如果失衡,则找到最上端的替罪节点,重构这棵树。由于点分树的子树就是原树的连通块结构,因此可以直接对这部分建出点分树以重构。

实现起来稍微有点复杂,可以使用vector什么的减小代码复杂度。

查询时按照上面的方法查询即可,需要使用倍增LCA求两点距离。

时间复杂度$O(n\log^3n)=O(能过)$

还是需要内存回收之类的保证空间复杂度。

  1. #include <queue>
  2. #include <cstdio>
  3. #include <vector>
  4. #include <algorithm>
  5. #define N 100010
  6. using namespace std;
  7. struct sbt
  8. {
  9. int w , ls , rs , si;
  10. }a[20000010];
  11. queue<int> q;
  12. vector<int> c[N];
  13. int head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][20] , len[N] , deep[N] , log[N];
  14. int r[N] , pre[N] , si[N] , mx[N] , sum , root , ra[N] , rb[N] , vis[N];
  15. long long ans;
  16. void add(int x , int y)
  17. {
  18. to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
  19. }
  20. void zig(int &k)
  21. {
  22. int t = a[k].ls;
  23. a[k].ls = a[t].rs , a[t].rs = k , a[t].si = a[k].si , a[k].si = a[a[k].ls].si + a[a[k].rs].si + 1;
  24. k = t;
  25. }
  26. void zag(int &k)
  27. {
  28. int t = a[k].rs;
  29. a[k].rs = a[t].ls , a[t].ls = k , a[t].si = a[k].si , a[k].si = a[a[k].ls].si + a[a[k].rs].si + 1;
  30. k = t;
  31. }
  32. void maintain(int &k , bool flag)
  33. {
  34. if(!flag)
  35. {
  36. if(a[a[a[k].ls].ls].si > a[a[k].rs].si) zig(k);
  37. else if(a[a[a[k].ls].rs].si > a[a[k].rs].si) zag(a[k].ls) , zig(k);
  38. else return;
  39. }
  40. else
  41. {
  42. if(a[a[a[k].rs].rs].si > a[a[k].ls].si) zag(k);
  43. else if(a[a[a[k].rs].ls].si > a[a[k].ls].si) zig(a[k].rs) , zag(k);
  44. else return;
  45. }
  46. maintain(a[k].ls , 0) , maintain(a[k].rs , 1);
  47. maintain(k , 0) , maintain(k , 1);
  48. }
  49. void insert(int &k , int x)
  50. {
  51. if(!k) k = q.front() , q.pop() , a[k].w = x , a[k].si = 1;
  52. else
  53. {
  54. a[k].si ++ ;
  55. if(x < a[k].w) insert(a[k].ls , x);
  56. else insert(a[k].rs , x);
  57. maintain(k , x >= a[k].w);
  58. }
  59. }
  60. int find(int k , int x)
  61. {
  62. if(!k) return 0;
  63. else if(x <= a[k].w) return find(a[k].ls , x) + a[a[k].rs].si + 1;
  64. else return find(a[k].rs , x);
  65. }
  66. void del(int &k)
  67. {
  68. if(!k) return;
  69. del(a[k].ls) , del(a[k].rs);
  70. a[k].w = a[k].si = 0 , q.push(k) , k = 0;
  71. }
  72. int lca(int x , int y)
  73. {
  74. int i;
  75. if(len[x] < len[y]) swap(x , y);
  76. for(i = log[len[x] - len[y]] ; ~i ; i -- )
  77. if(len[x] - len[y] >= (1 << i))
  78. x = fa[x][i];
  79. if(x == y) return x;
  80. for(i = log[len[x]] ; ~i ; i -- )
  81. if(len[x] >= (1 << i) && fa[x][i] != fa[y][i])
  82. x = fa[x][i] , y = fa[y][i];
  83. return fa[x][0];
  84. }
  85. int dis(int x , int y)
  86. {
  87. return deep[x] + deep[y] - 2 * deep[lca(x , y)];
  88. }
  89. int query(int x)
  90. {
  91. int i , ans = 0;
  92. for(i = x ; i ; i = pre[i])
  93. ans += find(ra[i] , dis(x , i) - r[x]);
  94. for(i = x ; pre[i] ; i = pre[i])
  95. ans -= find(rb[i] , dis(x , pre[i]) - r[x]);
  96. return ans;
  97. }
  98. void dfs(int x , int fa , int p)
  99. {
  100. int i;
  101. si[x] = 1 , c[p].push_back(x) , insert(ra[p] , r[x] - dis(x , p));
  102. if(pre[p]) insert(rb[p] , r[x] - dis(x , pre[p]));
  103. for(i = head[x] ; i ; i = next[i])
  104. if(!vis[to[i]] && to[i] != fa)
  105. dfs(to[i] , x , p) , si[x] += si[to[i]];
  106. }
  107. void getroot(int x , int fa)
  108. {
  109. int i;
  110. si[x] = 1 , mx[x] = 0;
  111. for(i = head[x] ; i ; i = next[i])
  112. if(!vis[to[i]] && to[i] != fa)
  113. getroot(to[i] , x) , si[x] += si[to[i]] , mx[x] = max(mx[x] , si[to[i]]);
  114. mx[x] = max(mx[x] , sum - si[x]);
  115. if(mx[x] < mx[root]) root = x;
  116. }
  117. void solve(int x , int from)
  118. {
  119. int i;
  120. del(ra[x]) , del(rb[x]) , c[x].clear();
  121. pre[x] = from , vis[x] = 1 , dfs(x , 0 , x);
  122. for(i = head[x] ; i ; i = next[i])
  123. if(!vis[to[i]])
  124. sum = si[to[i]] , root = 0 , getroot(to[i] , 0) , solve(root , x);
  125. }
  126. void join(int x)
  127. {
  128. int i , last = -1;
  129. for(i = x ; i ; i = pre[i])
  130. {
  131. c[i].push_back(x) , insert(ra[i] , r[x] - dis(x , i));
  132. if(pre[i]) insert(rb[i] , r[x] - dis(x , pre[i]));
  133. si[i] ++ ;
  134. if(pre[i] && si[i] * 4 > (si[pre[i]] + 1) * 3) last = pre[i];
  135. }
  136. if(~last)
  137. {
  138. for(i = 0 ; i < (int)c[last].size() ; i ++ ) vis[c[last][i]] = 0;
  139. sum = si[last] , root = 0 , getroot(last , 0) , solve(root , pre[last]);
  140. }
  141. }
  142. int main()
  143. {
  144. int n , i , j , f , b;
  145. for(i = 1 ; i <= 20000000 ; i ++ ) q.push(i);
  146. scanf("%*d%d%*d%*d%d" , &n , &r[1]);
  147. puts("0");
  148. insert(ra[1] , r[1]) , c[1].push_back(1) , si[1] = vis[1] = 1 , mx[0] = 1 << 30;
  149. for(i = 2 ; i <= n ; i ++ )
  150. {
  151. scanf("%d%d%d" , &f , &b , &r[i]) , f ^= (ans % 1000000000) , log[i] = log[i >> 1] + 1 , vis[i] = 1;
  152. fa[i][0] = pre[i] = f , len[i] = len[f] + 1 , deep[i] = deep[f] + b;
  153. add(f , i) , add(i , f);
  154. for(j = 1 ; (1 << j) <= len[i] ; j ++ ) fa[i][j] = fa[fa[i][j - 1]][j - 1];
  155. ans += query(i) , printf("%lld\n" , ans);
  156. join(i);
  157. }
  158. return 0;
  159. }

【bzoj3435】[Wc2014]紫荆花之恋 替罪点分树套SBT的更多相关文章

  1. 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT

    [BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...

  2. BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)

    题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...

  3. BZOJ3435: [Wc2014]紫荆花之恋(替罪羊树,Treap)

    Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是 ...

  4. bzoj3435 [Wc2014]紫荆花之恋

    如果这棵树不变的话,就是一个裸的点分树套平衡树,式子也很好推$di+dj<=ri+rj$,$ri-di>=dj-rj$ 平衡树维护$dj-rj$,然后查$ri-di$的$rank$即可. ...

  5. bzoj3435 [Wc2014]紫荆花之恋(动态点分治+替罪羊树)

    传送门(权限) 传送门(非权限) 题解 我终终终终终终于做出来啦!!! 作为一个没有学过替罪羊树的蒟蒻现场学了一下替罪羊树,作为一个平衡树都写数组版本的看着大佬的指针题解无语只能硬去理解然后照着抄了一 ...

  6. luoguP3920 [WC2014]紫荆花之恋 动态点分治 + 替罪羊树

    意外的好写..... 考虑点分 \(dis(i, j) \leq r_i + r_j\) 对于过分治中心一点\(u\),有 \(dis(i, u) - r_i = dis(j, u) + r_j\) ...

  7. bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 && AC400

    3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec  Memory Limit: 512 MBSubmit: 159  Solved: 40[Submit][Status] ...

  8. BZOJ 3435: [Wc2014]紫荆花之恋

    二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 ...

  9. BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋

    https://www.lydsy.com/JudgeOnline/problem.php?id=3435 https://www.luogu.org/problemnew/show/P3920 ht ...

随机推荐

  1. bzoj3312: [Usaco2013 Nov]No Change

    题意: K个硬币,要买N个物品.K<=16,N<=1e5 给定买的顺序,即按顺序必须是一路买过去,当选定买的东西物品序列后,付出钱后,货主是不会找零钱的.现希望买完所需要的东西后,留下的钱 ...

  2. POJ-1459 Power Network---最大流

    题目链接: https://cn.vjudge.net/problem/POJ-1459 题目大意: 简单的说下题意(按输入输出来讲,前面的描述一堆的rubbish,还用来误导人),给你n个点,其中有 ...

  3. 【BZOJ1064】[NOI2008] 假面舞会(图上DFS)

    点此看题面 大致题意:有\(k\)种面具(\(k\)是一个未知数且\(k≥3\),每种面具可能有多个),已知戴第\(i\)种面具的人能看到第\(i+1\)种面具上的编号,特殊的,戴第\(k\)种面具的 ...

  4. SqlServer 学习笔记

    随机函数 select rand() declare @age int set @age = rand()*100 select @age 数据类型转换 declare @birthday datat ...

  5. winform下读取excel文件并绑定datagridview例子

    首先我要读取这个excel文件然后生成Datable 用winform编程的方式 前台界面: 后台的代码 using System; using System.Collections.Generic; ...

  6. display:inline-block解决文字有间隙问题

    定义:display:inline-block是使元素以块级元素的形式呈现在行内.意思就是说,让这个元素显示在同一行不换行,但是又可以控制高度和宽度,这相当于内联元素的增强. 但是display:in ...

  7. mysql基础,DISTINCT关键字

  8. LeetCode946-验证栈序列

    问题:验证栈序列 给定 pushed 和 popped 两个序列,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true:否则,返回 false . 示例 ...

  9. JZOJ 3493. 【NOIP2013模拟联考13】三角形

    3493. [NOIP2013模拟联考13]三角形(triangle) (File IO): input:triangle.in output:triangle.out Time Limits: 10 ...

  10. python-字符串数据类型内置方法

    字符串类型内置方法 (str) 用途:描述性质的东西,如人的名字.单个爱好.地址.国家等 定义:使用单引号(' ').双引号(" ").三单引号(''' ''').三双引号(&qu ...