2018年论文题,上接loj2506,主要是论文中的第4章,也可快速跳至原题解

5.平衡树的嵌套问题

平衡树嵌套

所谓平衡树嵌套,就是若干棵平衡树,其中若干棵平衡树的根会指向另一颗平衡树上的一个节点

定义一棵平衡树的$W$为其子树内所有节点的$w_{x}$之和,再定义$w_{x}$为上述指向其的根的平衡树的$W$之和加1

(这两者定义并不嵌套,显然可以按层去计算$w_{x}$和$W$)

如果在一个平衡树中访问节点$x$的复杂度为$o(\log\frac{W}{w_{x}})$,那么就有以下结论——

定理9:以此法从全局的根访问一个节点$x$,复杂度为$o(k+\log n)$(其中$k$为从$x$到全局的根路径上所经过的平衡树数量,也即层数)

证明:从下往上依次记录在每一棵平衡树中的复杂度,即$\sum_{i=1}^{k}\log\frac{W_{i}}{w_{i}}+1$

根据定义,有$w_{i}\ge W_{i-1}$,不妨将其放缩,即$\sum_{i=1}^{k}\log\frac{W_{i}}{W_{i-1}}+1\sim o(k+\log n)$

而如果没有上述性质,即每一次都是$o(\log n)$的,那么总复杂度也就是$o(k\log n)$

下面,我们将要分别考虑Treap和Splay如何做到$o(\log\frac{W}{w_{x}})$的复杂度

Treap

考虑一个简单的构造,令节点$x$的随机值为$w_{x}$个随机值中的最大值

**定理10:**以此法随机并维护,节点$x$的期望深度为$o(\log\frac{W}{w_{x}})$

**证明:**为了方便表示,将节点按元素从小到大依次记为$a_{1},a_{2},...,a_{n}$,权值依次为$w_{1},w_{2},...,w_{n}$

类似前面,节点$a_{x}$的期望深度也就是有所有节点是其祖先的概率之和,即
$$
\sum_{i=1}^{x-1}\frac{w_{i}}{\sum_{j=i}^{x}w_{j}}+\sum_{i=x+1}^{n}\frac{w_{i}}{\sum_{j=x}^{i}w_{j}}
$$
(关于这里的概率,实际上也就是$\sum_{j=i}^{x}w_{j}$个随机值中最大值在$i$随机的$w_{i}$个数中,即此式)

将其展开,并进行放缩,即
$$
\le \sum_{i=1}^{x-1}\sum_{k=1}^{w_{i}}\frac{1}{\sum_{j=i+1}^{x}w_{j}+k}+\sum_{i=x+1}^{n}\sum_{k=1}^{w_{i}}\frac{1}{\sum_{j=x}^{i-1}w_{j}+k}
$$
每一次也就是一个区间,且这些区间恰好是连续的,即
$$
\sum_{i=w_{x}+1}^{\sum_{j=1}^{x}w_{j}}\frac{1}{i}+\sum_{i=w_{x}+1}^{\sum_{j=x}^{n}w_{j}}\frac{1}{i}\sim o(\log \frac{W}{w_{x}})
$$
当然这样随机的复杂度为$o(W)$,在某些情况下可能无法接受

一个简单的做法是记录子树的$w$之和,并以此为概率进行比较

另外还有一个比较有趣的做法,来实现这个随机——

对于一个随机变量$X$,令$P(x)$表示随机变量$X\le x$的概率

再构造一个函数$f(x)$,在其定义域中有$P(x)$的概率为函数值小于等于$x$,那么$f(x)$也就是一个随机$X$的函数,即只要在其定义域中等概率随机一个$x$,取$X=f(x)$即可

事实上,取$f(x)=P^{-1}(x)$即可,代入本题中,即$P(x)=x^{w}$,那么$f(x)=x^{\frac{1}{w}}$

考虑$x^{\frac{1}{w_{1}}}<y^{\frac{1}{w_{2}}}$的概率,也就是$x^{w2}<y^{w1}$

Splay

定理11:对节点$x$进行Splay操作的均摊复杂度为$o(\log\frac{W}{w_{x}})$

证明:令每一个节点的$sz_{x}$为子树内所有节点的权值之和,类似于结论2的证明,可以得到该结论

6.平衡树嵌套的应用

树链剖分

用平衡树来维护一条重链,对于顶端节点不是根节点的重链,将其连向顶端节点的父亲

根据轻边的性质,不难证明$k\sim o(\log n)$,因此单次复杂度仅为$o(\log n)$,事实上大部分使用线段树维护树链剖分都可以通过这样的技巧优化来减少一个$\log$

LCT

定理12:LCT的单次操作时间复杂度为均摊$o(\log n)$

证明:对于$o(k+\log n)$的复杂度,后者显然可以忽略, 对前者$k$进行势能分析:

(以下描述中,轻重儿子是根据子树大小确定,实虚儿子是根据LCT确定)

定义当前这个结构的势能为其中实儿子与重儿子不同的节点数(重儿子任取),考虑将一个节点到根路径上所有虚边都变为实边,对这个节点分类讨论:

1.若其是轻儿子,显然这样的节点数不多于$\log n$个,且每一个节点最多导致1次操作以及1个势能,即单个节点是$o(1)$的,那么总共即$o(\log n)$的

2.若其是重儿子,那么将实儿子修改为其,会导致势能减小1,即本次操作无贡献

且由于势能大小为不超过$n$的非负整数,因此即在最外面还会有一个$o(n)$,也可以接受

(另外,这也就说明了LCT上访问一个节点必须access,而不能直接去访问)

关于Treap维护LCT

看似Treap也可以维护LCT,但实际上并不能实现

这是因为在维护过程中,我们需要修改一个节点的$w_{x}$,在Splay中我们只需要将其Splay至根即可,但在Treap中我们要给其新的随机值并进行调整

首先,直接暴力删除再插入,也可以做到$o(\log n)$,那么也就是单次$o(k\log n)$,对$k$进行一样的均摊分析,也就得到了Treap维护LCT单次操作是$o(\log^{2}n)$的

当然,我们也可以略微优化这个调整(但似乎并不能该边其维护LCT的复杂度),若其随机值增加即将其下旋(将随机值较小的旋上来),否则将其上旋

那么这样的复杂度也就是其与最终状态的期望深度差,有以下结论——

定理13:假设其权值由$w$变为$w'$,则其期望深度差为$\log\frac{\max(w,w')}{\min(w,w')}$

证明:与之前一样,假设权值依次为$w_{1},w_{2},...,w_{n}$,且其中$w_{x}$的权值增加了$\Delta$($\Delta\ge 0$),我们要证明的也就是深度差是$o(\log \frac{w_{x}+\Delta}{w_{x}})$

深度也用每一个点的概率来表示,即
$$
\sum_{i=1}^{x-1}\frac{w_{i}}{\sum_{j=i}^{x}w_{j}}-\frac{w_{i}}{\sum_{j=i}^{x}w_{j}+\Delta}+\sum_{i=x+1}^{n}\frac{w_{i}}{\sum_{j=x}^{i}w_{j}}-\frac{w_{i}}{\sum_{j=x}^{i}w_{j}+\Delta}
$$
注意到$f(x)=\frac{1}{x}-\frac{1}{x+\Delta}$在$x>0$时是递减的,那么就可以与之前一样,将前者表示为
$$
\frac{w_{i}}{\sum_{j=i}^{x}w_{j}}-\frac{w_{i}}{\sum_{j=i}^{x}w_{j}+\Delta}=w_{i}\times f(\sum_{j=i}^{x}w_{j})\le \sum_{k=1}^{w_{i}}f(\sum_{j=i+1}^{x}w_{j}+k)=\sum_{i=w_{x}+1}^{\sum_{j=1}^{x}w_{j}}f(i)
$$
将$f(x)=\frac{1}{x}-\frac{1}{x+\Delta}$代入后分别计算,即$o(\log\frac{S}{w_{x}}-\log\frac{S}{w_{x}+\Delta})=o(\log\frac{w_{x}+\Delta}{w_{x}})$(其中$S=\sum_{j=1}^{x}w_{j}$)

后半部分类似,也可以得到该结论,另外将$\Delta<0$看作反过来即可

这个结论虽然并不能让Treap维护LCT的理论复杂度更优,但可能会有用处

原题解:

将表达式其看作一个$2n-1$的序列,建立一棵Treap使得其中序遍历即该序列,并且保证父亲节点运算优先级不大于儿子(其中元素看作$k+1$级的操作),那么不难证明此时不断合并即可得到结果

进一步的,考虑对于与这棵Treap根节点相连的连通块,必然是优先级最低的所有操作,且之后剩下的若干个也同样满足此性质Treap,这也就构成了嵌套的关系,根据定理9每一个节点深度是$o(k+\log n)$的

(一个节点子树内所有节点的$w$之和,不难发现也就是其Treap的子树大小,以此为概率进行比较)

综上,使用合并和分裂维护,复杂度都是深度,即$o(k+\log n)$,可以通过

  1. 1 #include "expr.h"
  2. 2 #include<bits/stdc++.h>
  3. 3 using namespace std;
  4. 4 #define N 20005
  5. 5 #define s(p) f[k].ch[p]
  6. 6 struct node{
  7. 7 int op,sz,tag,ch[2];
  8. 8 Data val;
  9. 9 bool operator < (const node &k)const{
  10. 10 return (op<k.op)||(op==k.op)&&(rand()%(sz+k.sz)<sz);
  11. 11 }
  12. 12 }f[N*1000];
  13. 13 int V,m,a,b,c,rt[N];
  14. 14 int New(int k){
  15. 15 f[++V]=f[k];
  16. 16 return V;
  17. 17 }
  18. 18 void rev(int &k){
  19. 19 k=New(k);
  20. 20 f[k].tag^=1;
  21. 21 swap(s(0),s(1));
  22. 22 }
  23. 23 void up(int k){
  24. 24 f[k].sz=f[s(0)].sz+f[s(1)].sz+1;
  25. 25 if ((s(0))&&(s(1)))f[k].val=F(f[s(0)].val,f[s(1)].val,f[k].op);
  26. 26 else{
  27. 27 if (s(0))f[k].val=f[s(0)].val;
  28. 28 if (s(1))f[k].val=f[s(1)].val;
  29. 29 }
  30. 30 }
  31. 31 void down(int k){
  32. 32 if (f[k].tag){
  33. 33 if (s(0))rev(s(0));
  34. 34 if (s(1))rev(s(1));
  35. 35 f[k].tag=0;
  36. 36 }
  37. 37 }
  38. 38 int merge(int x,int y){
  39. 39 if ((!x)||(!y))return x+y;
  40. 40 down(x),down(y);
  41. 41 int k;
  42. 42 if (f[x]<f[y]){
  43. 43 k=New(x);
  44. 44 s(1)=merge(f[x].ch[1],y);
  45. 45 }
  46. 46 else{
  47. 47 k=New(y);
  48. 48 s(0)=merge(x,f[y].ch[0]);
  49. 49 }
  50. 50 up(k);
  51. 51 return k;
  52. 52 }
  53. 53 void split(int k,int x,int &a,int &b){
  54. 54 if (!k){
  55. 55 a=b=0;
  56. 56 return;
  57. 57 }
  58. 58 down(k);
  59. 59 if (x<=f[s(0)].sz){
  60. 60 b=New(k);
  61. 61 split(s(0),x,a,f[b].ch[0]);
  62. 62 up(b);
  63. 63 }
  64. 64 else{
  65. 65 a=New(k);
  66. 66 split(s(1),x-f[s(0)].sz-1,f[a].ch[1],b);
  67. 67 up(a);
  68. 68 }
  69. 69 }
  70. 70 void init(int id,int n,int m,int k,const Data *a,const int *op){
  71. 71 srand(time(0));
  72. 72 V=2*n-1;
  73. 73 for(int i=1;i<2*n;i++){
  74. 74 if (i%2==0)f[i].op=op[i/2];
  75. 75 else{
  76. 76 f[i].val=a[i/2];
  77. 77 f[i].op=k+1;
  78. 78 }
  79. 79 f[i].sz=1;
  80. 80 rt[0]=merge(rt[0],i);
  81. 81 }
  82. 82 }
  83. 83 Data modify_data(int id,int x,Data y){
  84. 84 x=x*2+1;
  85. 85 split(rt[id],x-1,a,b);
  86. 86 split(b,1,b,c);
  87. 87 b=New(b);
  88. 88 f[b].val=y;
  89. 89 rt[++m]=merge(merge(a,b),c);
  90. 90 return f[rt[m]].val;
  91. 91 }
  92. 92 Data modify_op(int id,int x,int y){
  93. 93 x*=2;
  94. 94 split(rt[id],x-1,a,b);
  95. 95 split(b,1,b,c);
  96. 96 b=New(b);
  97. 97 f[b].op=y;
  98. 98 rt[++m]=merge(merge(a,b),c);
  99. 99 return f[rt[m]].val;
  100. 100 }
  101. 101 Data reverse(int id,int x,int y){
  102. 102 x=x*2+1,y=y*2+1;
  103. 103 split(rt[id],x-1,a,b);
  104. 104 split(b,y-x+1,b,c);
  105. 105 rev(b);
  106. 106 rt[++m]=merge(merge(a,b),c);
  107. 107 return f[rt[m]].val;
  108. 108 }

(似乎被卡了操作次数)

[uoj173]鏖战表达式的更多相关文章

  1. wc2016鏖战表达式(可持久treap)

    由运算符有优先级可以想到先算优先级小的,然后两边递归,但符号比较少,有大量相同的,同级之间怎么办呢?因为运算符满足结合律,同级之间选一个然后两边递归也是没问题的,然后我们想到用fhqtreap进行维护 ...

  2. WC2016自测

    挑战NPC 原题链接 爆搜20分,贪心10分,网络流30分 //挑战NPC #include <cstdio> #include <cstring> #include < ...

  3. 2017-12 CDQZ集训(已完结)

    从联赛活了下来(虽然分数倒一……),接下来要去CDQZ集训啦…… DAY -2 2017-12-16 被老师安排负责一部分同学的住宿以及安排…… 抓紧时间继续学习,LCT真好玩啊真好玩…… 晚上放假了 ...

  4. 转:鏖战双十一-阿里直播平台面临的技术挑战(webSocket, 敏感词过滤等很不错)

    转自:http://www.infoq.com/cn/articles/alibaba-broadcast-platform-technology-challenges 鏖战双十一-阿里直播平台面临的 ...

  5. 【.net 深呼吸】细说CodeDom(2):表达式、语句

    在上一篇文章中,老周厚着脸皮给大伙介绍了代码文档的基本结构,以及一些代码对象与CodeDom类型的对应关系. 在评论中老周看到有朋友提到了 Emit,那老周就顺便提一下.严格上说,Emit并不是针对代 ...

  6. 你知道C#中的Lambda表达式的演化过程吗?

    那得从很久很久以前说起了,记得那个时候... 懵懂的记得从前有个叫委托的东西是那么的高深难懂. 委托的使用 例一: 什么是委托? 个人理解:用来传递方法的类型.(用来传递数字的类型有int.float ...

  7. 再讲IQueryable<T>,揭开表达式树的神秘面纱

    接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...

  8. Linq表达式、Lambda表达式你更喜欢哪个?

    什么是Linq表达式?什么是Lambda表达式? 如图: 由此可见Linq表达式和Lambda表达式并没有什么可比性. 那与Lambda表达式相关的整条语句称作什么呢?在微软并没有给出官方的命名,在& ...

  9. 背后的故事之 - 快乐的Lambda表达式(一)

    快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...

随机推荐

  1. 暑期 2021 | Serverless Devs 最全项目申请攻略来啦!

    Serverless 是近年来云计算领域热门话题,凭借极致弹性.按量付费.降本提效等众多优势受到很多人的追捧,各云厂商也在不断地布局 Serverless 领域.但是随着时间的发展,Serverles ...

  2. 题解 Division Game

    link Description 懒得写了. Solution 设 \(f(x)\) 表示对于一个位置操作了 \(x\) 次后刚好变为 \(1\) 的方案数,可以看出的是 \(f(x)\) 同样也是对 ...

  3. Go语言核心36讲(Go语言进阶技术二)--学习笔记

    08 | container包中的那些容器 我们在上次讨论了数组和切片,当我们提到数组的时候,往往会想起链表.那么 Go 语言的链表是什么样的呢? Go 语言的链表实现在标准库的container/l ...

  4. JS最简单的定时累加计数器

    js代码: 1 var timer , k = 0; 2 function star() { 3 k += 1; 4 document.getElementById("num"). ...

  5. 项目实战:Qt文件改名工具 v1.2.0(支持递归检索,搜索:模糊匹配,前缀匹配,后缀匹配;重命名:模糊替换,前缀追加,后缀追加)

    需求   在整理文件和一些其他头文件的时候,需要对其名称进行整理和修改,此工具很早就应该写了,创业后,非常忙,今天抽空写了一个顺便提供给学习.   工具和源码下载地址   本篇文章的应用包和源码包可在 ...

  6. Java:AQS 小记-1(概述)

    Java:AQS 小记-1(概述) 概述 全称是 Abstract Queued Synchronizer(抽象队列同步器),是阻塞式锁和相关的同步器工具的框架,这个类在 java.util.conc ...

  7. [调试笔记] 晚测5 T1 容易题

    众所周知,sbwzx在考试一结束就嚷嚷T1是个sb题.那他为什么调了2小时才调出来呢?快和小编一起看看吧. Sb题:指除了sbwzx别人都能做出来的题 1.CE:震惊!sbwzx竟然连map都不会用, ...

  8. 嵌入式单片机stm32之DMA实验

    一. 对于大容量的STM32芯片有2个DMA控制器,控制器1有7个通道,控制器2有5个通道 每个通道都可以配置一些外设的地址. 二. 通道的配置过程: 1. 首先设置CPARx寄存器和CMARx寄存器 ...

  9. JavaScript中的this对象指向理解

    在JavaScript中,this不是固定不变的,它的指向取决于上下文环境,一般的,认为this指向使用它时所在的对象.主要有以下几类指向: 在方法中,this 表示该方法所属的对象. 如果单独使用, ...

  10. Linux修改bashrc

    .bashrc是一个隐藏的文件,要打开并修改该文件需要: (1) 查看:ll -a 找到文件 .bashrc: (2) 打开:vi .bashrc (或者 vim .bashrc) 打开文件: (3) ...