题目描述

输入

输出

样例输入

4 5
1 3 2 5
1 2
1 3
2 4
4 2 4
1 2 4
2 3 4
3 1 4 1
4 1 4

样例输出

16/3
6/1


题解

LCT区间合并

前三个操作都是LCT的基本操作,可以LCT水过;重点在于第四个操作。

考虑一个长度为n的序列,它的子区间个数为$\sum\limits_{i=1}^ni=\frac{n(n-1)}2$,只需要维护每个子区间的长度之和即可。

考虑如果已经知道了左右子树的信息以及当前节点信息,如何更新当前子树的信息。需要解决区间合并问题。

答案除了原来两子树答案之和以外,考虑1~n序列中的第i个点,它对答案的贡献是 左边个数*右边个数$i(n-i+1)$ ,而在合并后它右边的元素多了$si[rs]+1$,故答案增加了$\sum\limits_{i\in ls}rank[i]*w[i]$。所以需要记录一个数组$lv[]$,它表示 子树中每个点的权值*该点在子树中从左向右的排名(正排名) 之和。

对于右边的点,同理,记录一个$rv[]$表示 子树中每个点的权值*该点在子树中从右向左的排名(逆排名) 之和。

于是就可以使用这两个数组更新总答案。

然后考虑怎么更新这两个数组,$lv$数组pushup时只涉及到当前节点和右子树的节点,每个节点的排名增加了$si[ls]+1$,所以$lv$除了两子树的$lv$之和外还要加上$(w[x]+sum[rs])*(si[ls]+1)$。

所以对于每个节点,维护$si,w,sum,lv,rv,tv$,分别为子树大小、当前节点权值、当前子树权值和、权值*正排名、权值*逆排名、答案(子区间总长)。

然后思考add&pushdown怎样进行:$sum$包含$si$个节点的贡献,所以加上$si*tag$;$lv,rv$包含$\sum\limits_{i=1}^{si}i=\frac{si(si+1)}2$个节点的贡献,所以加上$\frac{si(si+1)*tag}2$;$tv$包含$\sum\limits_{i=1}^n\sum\limits_{j=1}^ij=\frac{n(n+1)(n+2)}6$的贡献,所以加上$\frac{n(n+1)(n+2)*tag}6$。

知道了pushup和add&pushdown怎么写以后本题就水了,直接split出链后取出$tv$,与$\frac{si(si+1)}2$作比即为答案。

但 是 有 一 点 需 要 注 意 , 写 findroot 时 必 须 找 Splay tree 的 根 , 而 不 是 原 树 根 , 否 则 会 因 为 某 些 玄 学 原 因 而 无 限 TLE !

感觉说了这么多也没有直接看代码来的直白~

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. #define N 50010
  5. using namespace std;
  6. typedef long long ll;
  7. int fa[N] , c[2][N] , rev[N];
  8. ll si[N] , w[N] , sum[N] , lv[N] , rv[N] , tv[N] , tag[N];
  9. void rever(int x)
  10. {
  11. swap(c[0][x] , c[1][x]) , swap(lv[x] , rv[x]) , rev[x] ^= 1;
  12. }
  13. void add(int x , ll a)
  14. {
  15. w[x] += a , sum[x] += a * si[x];
  16. lv[x] += a * si[x] * (si[x] + 1) / 2;
  17. rv[x] += a * si[x] * (si[x] + 1) / 2;
  18. tv[x] += a * si[x] * (si[x] + 1) * (si[x] + 2) / 6;
  19. tag[x] += a;
  20. }
  21. void pushup(int x)
  22. {
  23. int l = c[0][x] , r = c[1][x];
  24. si[x] = si[l] + si[r] + 1;
  25. sum[x] = sum[l] + sum[r] + w[x];
  26. lv[x] = lv[l] + lv[r] + (w[x] + sum[r]) * (si[l] + 1);
  27. rv[x] = rv[l] + rv[r] + (w[x] + sum[l]) * (si[r] + 1);
  28. tv[x] = tv[l] + tv[r] + lv[l] * (si[r] + 1) + rv[r] * (si[l] + 1) + w[x] * (si[l] + 1) * (si[r] + 1);
  29. }
  30. void pushdown(int x)
  31. {
  32. int l = c[0][x] , r = c[1][x];
  33. if(rev[x]) rever(l) , rever(r) , rev[x] = 0;
  34. if(tag[x]) add(l , tag[x]) , add(r , tag[x]) , tag[x] = 0;
  35. }
  36. bool isroot(int x)
  37. {
  38. return c[0][fa[x]] != x && c[1][fa[x]] != x;
  39. }
  40. void update(int x)
  41. {
  42. if(!isroot(x)) update(fa[x]);
  43. pushdown(x);
  44. }
  45. void rotate(int x)
  46. {
  47. int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1;
  48. if(!isroot(y)) c[c[1][z] == y][z] = x;
  49. fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;
  50. pushup(y) , pushup(x);
  51. }
  52. void splay(int x)
  53. {
  54. update(x);
  55. while(!isroot(x))
  56. {
  57. int y = fa[x] , z = fa[y];
  58. if(!isroot(y))
  59. {
  60. if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x);
  61. else rotate(y);
  62. }
  63. rotate(x);
  64. }
  65. }
  66. void access(int x)
  67. {
  68. int t = 0;
  69. while(x) splay(x) , c[1][x] = t , pushup(x) , t = x , x = fa[x];
  70. }
  71. int findroot(int x)
  72. {
  73. while(fa[x]) x = fa[x];
  74. return x;
  75. }
  76. void makeroot(int x)
  77. {
  78. access(x) , splay(x) , rever(x);
  79. }
  80. void link(int x , int y)
  81. {
  82. if(findroot(x) != findroot(y)) makeroot(x) , fa[x] = y;
  83. }
  84. void split(int x , int y)
  85. {
  86. makeroot(x) , access(y) , splay(y);
  87. }
  88. void cut(int x , int y)
  89. {
  90. split(x , y);
  91. if(fa[x] == y) fa[x] = c[0][y] = 0 , pushup(y);
  92. }
  93. ll gcd(ll a , ll b)
  94. {
  95. return b ? gcd(b , a % b) : a;
  96. }
  97. int main()
  98. {
  99. int n , m , i , opt , x , y;
  100. ll z , t;
  101. scanf("%d%d" , &n , &m);
  102. for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &w[i]) , pushup(i);
  103. for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , link(x , y);
  104. while(m -- )
  105. {
  106. scanf("%d%d%d" , &opt , &x , &y);
  107. if(opt == 1) cut(x , y);
  108. else if(opt == 2) link(x , y);
  109. else if(opt == 3)
  110. {
  111. scanf("%lld" , &z);
  112. if(findroot(x) == findroot(y)) split(x , y) , add(y , z);
  113. }
  114. else
  115. {
  116. if(findroot(x) == findroot(y))
  117. {
  118. split(x , y) , z = tv[y] , t = si[y] * (si[y] + 1) / 2;
  119. printf("%lld/%lld\n" , z / gcd(z , t) , t / gcd(z , t));
  120. }
  121. else puts("-1");
  122. }
  123. }
  124. return 0;
  125. }

【bzoj3091】城市旅行 LCT区间合并的更多相关文章

  1. bzoj3091 城市旅行 LCT + 区间合并

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3091 题解 调了整个晚自习才调出来的问题. 乍一看是个 LCT 板子题. 再看一眼还是个 LC ...

  2. BZOJ3091城市旅行——LCT区间信息合并

    题目描述 输入 输出 样例输入 4 5 1 3 2 5 1 2 1 3 2 4 4 2 4 1 2 4 2 3 4 3 1 4 1 4 1 4 样例输出 16/3 6/1 提示 对于所有数据满足 1& ...

  3. BZOJ3091 城市旅行 LCT

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3091 题意概括 鉴于本人语文不好,此题的描述原题很清晰,废话不多,请看原题. 可怕,原题是图片,不 ...

  4. BZOJ3091: 城市旅行(LCT,数学期望)

    Description Input Output Sample Input 4 5 1 3 2 5 1 2 1 3 2 4 4 2 4 1 2 4 2 3 4 3 1 4 1 4 1 4 Sample ...

  5. 【BZOJ3091】城市旅行 LCT

    [BZOJ3091]城市旅行 Description Input Output Sample Input 4 5 1 3 2 5 1 2 1 3 2 4 4 2 4 1 2 4 2 3 4 3 1 4 ...

  6. 【LCT】BZOJ3091 城市旅行

    3091: 城市旅行 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1927  Solved: 631[Submit][Status][Discuss ...

  7. bzoj 3091: 城市旅行 LCT

    题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3091 题解: 首先前三个操作就是裸的LCT模板 只考虑第四个操作. 要求我们计算期望,所以我 ...

  8. BZOJ 3091: 城市旅行 [LCT splay 期望]

    3091: 城市旅行 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1454  Solved: 483[Submit][Status][Discuss ...

  9. BZOJ 3091: 城市旅行 lct 期望 splay

    https://www.lydsy.com/JudgeOnline/problem.php?id=3091 https://blog.csdn.net/popoqqq/article/details/ ...

随机推荐

  1. 2018.10.03 NOIP+ 模拟赛 解题报告

    得分: \(30+5+0=35\)(考得真不咋滴) \(T1\):奥义商店(点此看题面) 以为很简单,对着这题想了一个多小时,最后果断打了个暴力交了... ... 看完题解发现其实也不是很难. 对于\ ...

  2. Java后台工程师的3次面试

    第一次面试 我面的是一个中小公司,在BOSS直聘上面找的,去之前看了看关于Java的一些基础知识,在牛客网上面看的,也做了一下牛客网的题目.然后跟HR约了一个时间就去面试了.因为第一次面试,一点经验都 ...

  3. js数据结构处理--------树结构数据遍历

    1.深度遍历 深度遍历利用栈来实现 class Stack { constructor () { this.top = 0, // 栈的长度 this.list = [] } push(item) { ...

  4. js打印div指定区域内容

    <script> function myPrint(obj){ var newWindow=window.open("打印窗口","_blank") ...

  5. Fight Against Traffic -简单dijkstra算法使用

    题目链接 http://codeforces.com/contest/954/problem/D 题目大意 n m s t 分别为点的个数, 边的个数,以及两个特殊的点 要求s与t间的距离在新增一条边 ...

  6. Linux IO调度方法

    目录 I/O调度的4种算法 I/O调度程序的测试 ionice IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调度器也被叫做电 ...

  7. Java泛型和反射

    1. 字节码对象的三种获取方式 以String为例 Class<? extends String> strCls = "".getClass(); Class<S ...

  8. 致敬wusir懒孩子自有懒孩子的生存之道之二

    https://www.cnblogs.com/wupeiqi/ https://www.cnblogs.com/Eva-J/ https://www.cnblogs.com/wupeiqi/p/90 ...

  9. 力扣题目汇总(丑数,重复N的元素,求众数)

    丑数 1.题目描述 编写一个程序判断给定的数是否为丑数. 丑数就是只包含质因数 2, 3, 5 的正整数. 示例 1: 输入: 6 输出: true 解释: 6 = 2 × 3 示例 2: 输入: 8 ...

  10. Union found

    Use array. class UnionFound { public: vector<int> v; int cnt; UnionFound(int n) { v = vector&l ...