题意

  求 \(n\) 个点的 Treap 深度为 \(h=0,1,2,\cdots,n\) 的概率。

  Treap 是一个随机二叉树,每个节点有权值和优先级,权值和优先级都是 \([0,1]\) 中的随机实数。niubi 的是,由于随机的实数精度足够高,你可以近似认为任意两个权值、任意两个优先级相同的概率是 \(0\)。

  \(n\le 30000\)

题解

  又是神题,我他吗都做不来

  官方题解大概是这样,但本蒟蒻完全没看懂,于是只好向 scb 大佬请教了另一种思考方法(得到的 dp 式子一样)。

  考虑 Treap 的构造方式:先随机选择 \(n\) 个权值,然后从空树开始,每次插入一个节点。插入操作如下:先随机一个优先级 \(p\),无视优先级,按照二叉搜索树的方式插入这个节点,然后考虑优先级,一直把这个节点往上旋转,直到满足优先级条件。

  若我们事先确定了每个权值的优先级,那么把权值按优先级从大到小排序,不难发现 Treap 的加点就变成了每次给树加一个叶子。由于 \(n\) 个数的大小关系不变时 Treap 的形态也不变,我们可以把 \(n\) 个权值离散化成 \(1\) 到 \(n\) 这 \(n\) 个整数,问题是完全等价的。

  权值序列的每个数都不是确定的,而是在 \([0,1]\) 任取一个实数,为什么可以离散化成 \(1\) 到 \(n\) 这 \(n\) 个整数?如何证明每种 \(1\) 到 \(n\) 的排列对应的原权值序列的数量相同?(不然离散化后算的概率不一样啊)

  遗憾的是,这个需要微积分等高数知识,过程也比较复杂,本蒟蒻不会简单证法。目前把这当成常识记住就好了。

  现在优先级已经没用了,我们只需要考虑每次给树加一个权值为 \([1,n]\) 内整数的叶子,这棵树要满足二叉搜索树的性质(即任意点的权值小于其左儿子,大于其右儿子)。求每种树高的出现概率。

  这就跟普通的求方案数类似,设 \(dp(i,j)\) 表示权值为 \(1,2,\cdots j\) 的点构成深度不大于 \(i\) 的树的概率,则我们枚举根的权值 \(k\),其左子树的权值集合为 \(\{1,2,\cdots,k-1\}\),概率就是 \(dp(i-1,k-1)\);其右子树的权值集合为 \(\{k+1,k+2,\cdots,j\}\),等价于集合 \(\{1,2,\cdots,j-k\}\),概率是 \(dp(i-1,j-k)\)。把所有 \(k\) 对应的概率求平均值,就得到了 \(dp(i,j)\)。$$dp(i,j)=\frac{1}{j} \sum\limits_{k=1}^j f(i-1,k-1)\times f(i-1,j-k)$$

  \(O(n^3)\) 转移可得 \(40\) 分。

  然后发现由于权值随机,而且 Treap 本身就是在随机优先级时树高维持在 \(\log n\) 级别的数据结构,故期望树高为 \(O(\log n)\),概率都会集中在这附近。实测大概只需要算到 \(dp(50,)\) 即可满足精度要求。\(O(50n^2)\) 可得 \(50\) 分。

  然后发现上式显然是个卷积形式,把 \(dp(i)\) 看成生成函数,转移就是 \(f(i)\) 自己卷自己,\(\text{FFT}\) 即可。复杂度 \(O(50n\log n)\),可得 \(100\) 分。

#include<bits/stdc++.h>
#define N 131075
using namespace std;
inline int read(){
int x=0; bool f=1; char c=getchar();
for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
if(f) return x;
return 0-x;
}
int n;
const double PI = acos(-1);
struct cp{
double r,i;
cp(){r=i=0;}
cp(double _r, double _i){r=_r, i=_i;}
friend cp operator + (cp a, cp b){return cp(a.r+b.r, a.i+b.i);}
friend cp operator - (cp a, cp b){return cp(a.r-b.r, a.i-b.i);}
friend cp operator * (cp a, cp b){return cp(a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r);}
friend cp operator / (cp a, double b){return cp(a.r/b, a.i/b);}
}dp[N];
struct Poly{
int n,bit,r[N];
void init(int x){
for(n=1,bit=0; n<x; n<<=1,++bit);
for(int i=1; i<n; ++i) r[i]=(r[i>>1]>>1)|((i&1)<<(bit-1));
}
void dft(cp *a, int f){
for(int i=0; i<n; ++i) if(i<r[i]) swap(a[i],a[r[i]]);
cp wn,w,x,y;
for(int i=1; i<n; i<<=1){
wn=cp(cos(PI/i),sin(f*PI/i));
for(int j=0; j<n; j+=i<<1){
w=cp(1,0);
for(int k=0; k<i; ++k,w=w*wn)
x=a[j+k], y=w*a[j+i+k],
a[j+k]=x+y, a[j+i+k]=x-y;
}
}
if(f==-1) for(int i=0; i<n; ++i) a[i]=a[i]/n;
}
}FFT;
int main(){
n=read();
FFT.init(n*2+1);
dp[0]=cp(1,0); double lst=0;
for(int scx=1; scx<=min(n,50); ++scx){
FFT.dft(dp,1);
for(int i=0; i<FFT.n; ++i) dp[i]=dp[i]*dp[i];
FFT.dft(dp,-1);
for(int i=n; i<FFT.n; ++i) dp[i]=cp(0,0);
for(int i=n; i>0; --i) dp[i]=dp[i-1]/i; dp[0]=cp(1,0);
printf("%.10lf\n",dp[n].r-lst);
lst=dp[n].r;
}
for(int i=min(n,50)+1; i<=n; ++i) printf("%.10lf\n",0);
return 0;
}

【未知来源】Randomized Binary Search Tree的更多相关文章

  1. 【XSY2332】Randomized Binary Search Tree 概率DP FFT

    题目描述 \(\forall 0\leq i<n\),求有多少棵\(n\)个点,权值和优先级完全随机的treap的树高为\(i\). \(n\leq 30000\) 题解 设\(f_{i,j}\ ...

  2. 【xsy2332】Randomized Binary Search Tree DP+FFT

    题目大意:给你一个$[0,1]$之间等概率随机序列,你需要把这个序列插入到一棵$treap$中,问这棵$treap$的期望深度,请对于$[1,n]$中的每个深度分别输出它的概率(实数,保留五位小数). ...

  3. [LeetCode]题解(python):098 Validate Binary Search Tree

    题目来源 https://leetcode.com/problems/validate-binary-search-tree/ Given a binary tree, determine if it ...

  4. 一道二叉树题的n步优化——LeetCode98validate binary search tree(草稿)

    树的题目,往往可以用到三种遍历.以及递归,因为其结构上天然地可以往深处递归,且判断条件也往往不复杂(左右子树都是空的). LeetCode 98题讲的是,判断一棵树是不是二叉搜索树. 题目中给的是标准 ...

  5. 【LeetCode】 99. Recover Binary Search Tree [Hard] [Morris Traversal] [Tree]

    Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...

  6. Lowest Common Ancestor of a Binary Search Tree(树中两个结点的最低公共祖先)

    题目描述: Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in ...

  7. [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法

    二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...

  8. Leetcode 笔记 99 - Recover Binary Search Tree

    题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...

  9. Leetcode 笔记 98 - Validate Binary Search Tree

    题目链接:Validate Binary Search Tree | LeetCode OJ Given a binary tree, determine if it is a valid binar ...

随机推荐

  1. server 2008 R2 DHCP服务器部署

    安装DHCP服务器 和上一篇文章中安装IIS 7.0一样,我们在安装DHCP服务器的时候也要用到Windows Server 2008的服务器安装器. 首先打开服务器管理器,点击开始菜单——>管 ...

  2. 【Adobe Air程序开发】eclipse安装flash builder 4.7插件以及java、flex整合开发

    看了看网上不少文章,发现很多内容都是很老的,没法用.故把自己的安装过程记录下来,方便以后使用 1.在这里,eclipse使用最新版eclipse juno 3.7 2.在adobe官网https:// ...

  3. Web工作方式

    我们平时浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要浏览的内容.在这个看似简单的用户行为背后,到底隐藏了些什么呢? 对于普通的上网过程,系统其实是这样做的:浏览器本身是一个 ...

  4. JS中常用的语法

    在做前端中,JS的语法尤为重要..没有它,就没有你的未来吧.. 下面将一些常用的JS语法给大家罗列出来.. 也给自己备份一下.. 以备不时之需.. 1.输出语句:document.write(&quo ...

  5. Linux文件属性之用户和组基础知识介绍

    一.Linux多用户多任务介绍 Linux/Unix 是一个多用户.多任务的操作系统:在讲Linux账号及账号组管理之前,我们先简单了解多用户.多任务操作系统的基本概念. 1.1Linux单用户多任务 ...

  6. Http 协议学习

    借助[小坦克:HTTP 协议教程] 1.HTTP协议是什么 协议是计算机在通信过程中必须共同遵守的规则,我的理解是类似所有汽车在行驶过程中必须共同遵守的交通规则一样. http协议叫超文本协议,是一种 ...

  7. 【hash表】图书管理

    [哈希和哈希表]图书管理 题目描述 图书管理是一件十分繁杂的工作,在一个图书馆中每天都会有许多新书加入.为了更方便的管理图书(以便于帮助想要借书的客人快速查找他们是否有他们所需要的书),我们需要设计一 ...

  8. X86逆向9:通过关键常量破解

    本章将讲解一下关于关键全局变量的一些内容,关键的全局变量对于软件的破解非常的有用,找到了关键全局变量并改写它同样可以完成完美爆破一个程序,这里我将使用CM小例子来讲解搜索关键变量的一些技巧,最后我们来 ...

  9. 【原创】大数据基础之Oozie(4)oozie使用的spark版本升级

    oozie默认使用的spark是1.6,一直没有升级,如果想用最新的2.4,需要自己手工升级 首先看当前使用的spark版本的jar # oozie admin -oozie http://$oozi ...

  10. 【原创】大叔经验分享(72)mysql时区

    查看当前时区 > show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value ...