题目
思路
这个“数”的比较,复杂度是很高的。如果我们可以 hash \text{hash} hash一下,那么就会变的很简单。
而且,更有意思的是,我们的新“数”是在原有的“数”的基础上产生的,就可以充分利用原有的 hash \text{hash} hash值。
我们用平衡树中的每一个点代表一个当前存在的“数”。即,键值为“数”。当然,满足平衡树的基本性质,中序遍历不降。对于这样一个平衡树,我们很容易的就能写出其 hash \text{hash} hash值。为方便保存,我们用一个有序数对 ( a , b ) (a,b) (a,b)来描述一个“数”( a , b ∈ R a,b\in\R a,b∈R)。
现在的问题是新的一个“数”。这样的一个“数”,可以利用原有的 hash \text{hash} hash值,将其写为 ( a , b ) (a,b) (a,b)( a , b ∈ R a,b\in\R a,b∈R)。而已经在平衡树中的“数”,我们也用的同样的方法保存,所以可以轻松的 O ( 1 ) \mathcal O(1) O(1)比较。这样一来,我们就 O ( log n ) \mathcal O(\log n) O(logn)的找到了应当插入的点。
糟糕的事情到了!新插入的点会导致整个子树的 hash \text{hash} hash值发生变化!
但是不怕,我们有 重量平衡树 treap \text{treap} treap 来解决问题。
小知识:重量平衡树。满足每次基本操作影响到的子树大小的 最坏情况 / / /均摊 / / /期望 为 O ( log n ) \mathcal O(\log n) O(logn)。其中著名的有替罪羊与 t r e a p treap treap(期望的计算,可以通过枚举到达哪一层来实现,概率为“修正值 f i x fix fix是当前子树的最大值”,即 1 s i z e \frac{1}{size} size1,而影响的大小为 s i z e size size,故期望为树高,即 O ( log n ) \mathcal O(\log n) O(logn))。
然后就搞定了!但是 hash \text{hash} hash值的改变,会导致某些点的 ( a , b ) (a,b) (a,b)发生改变—— ( a , b ) (a,b) (a,b)的本质是, f i r s t = first= first=“数” a , s e c o n d = a,second= a,second=“数” b b b的一个“数”。如果 a a a的 hash \text{hash} hash值改变了,我就得跟着改变。这样一来,复杂度又不正确了。
所以我们不再使用 ( a , b ) ( a , b ∈ R ) (a,b)(a,b\in\R) (a,b)(a,b∈R),转而使用 ( a , b ) ( a , b ∈ Z ∩ [ 0 , k ] ) (a,b)(a,b\in\Z\cap[0,k]) (a,b)(a,b∈Z∩[0,k]),其中 k k k是平衡树的节点数量。也就是说,我们不再使用 hash \text{hash} hash值,而是根据“数”的定义,将一个“数”表示为另外两个“数”。可以让 a = 0 a=0 a=0或者 b = 0 b=0 b=0,表示“数” 0 0 0。
然后我们就可以暴力维护了。时间复杂度 O ( n log n ) \mathcal O(n\log n) O(nlogn)。
代码
此处仅提供 i n s e r t insert insert的伪代码作为参考。
int id[]; // a_i对应平衡树中的id[i]节点
double f[]; // 每个平衡树节点的hash值
void treap::insert(pair p){
当前处理到了节点o
if(o == 0){
o = newNode(p.first,p.second);
return ; // 建新点,数据为p
}
if(make_pair(f[data[o][0]],f[data[o][1]]) < make_pair(f[p.first],f[p.second])
insert(右子树); // 数据都存储的是两个节点编号
else insert(左子树);
}
void insert(int l,int r,int k){
treap.insert(make_pair(id[l],id[r]));
将f进行调整 /* 可以使用类似线段树的方法,给每个节点一个开区间值域(L,R),然后
该点的hash值取mid=(L+R)/2,然后左右子树递归为(L,mid)与(mid,R) */
id[k] = cntNode; // cntNode是新申请的节点(即刚刚插入的节点)
}
然后对于平衡树,可以维护一个最大的 h a s h hash hash值(和其对应的 a a a的下标)。
a a a的下标,可以通过每一个点都存储一下做到。也就是说,
data[o][2] = index; // index是参数,对应的下标
MD我也只能口胡,我真的打不出来