【xsy2332】Randomized Binary Search Tree DP+FFT


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

$treap$的优先级之也是在$[0,1]$中等概率随机出来的。

ps:这个$[0,1]$的随机非常$niubi$,任意一个$[0,1]$间的实数被选中的概率是$0$

这一题有一个很特殊的性质:两个序列都是等概率随机出来的。

在没有相同数的情况下,我们发现$treap$最终的形态跟数值插入的顺序是没有关系的。

假如我们将整个序列排序,我们会发现这些元素被$treap$分配的优先级,是一个(随机的)随机数序列。

$treap$的形状等价于优先级序列的笛卡尔树。

所以我们可以列出一个$dp$:设$f[i][j]$表示$j$个点构成的笛卡尔树深度不大于$i$的概率。

考虑到每个点的值都是随机的,则有:$f[i][j]=\frac{1}{j}\sum\limits_{k=1}^{j}f[i-1][k-1]\times f[i-1][j-k]$

直接转移的概率显然是$O(n^3)$的。

我们发现,概率会集中在笛卡尔树期望深度附近,所以实际上并不需要DP到$f[n][]$,我们大概率只需要dp到$f[50][]$即可满足精度要求(剩下的都是0)

然后,发现上面的式子是一个卷积,我们可以用$FFT$加速转移。

所以最终的复杂度就变成了$O(An\log\  n)$

 1 #include
 2 #define M (1<<16)
 3 #define PI acos(-1)
 4 using namespace std;
 5 
 6 struct cp{
 7     double r,i; cp(){i=r=0;}
 8     cp(double R,double I){r=R; i=I;}
 9     friend cp operator +(cp a,cp b){return cp(a.r+b.r,a.i+b.i);}
10     friend cp operator -(cp a,cp b){return cp(a.r-b.r,a.i-b.i);}
11     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);}
12     friend cp operator /(cp a,double b){return cp(a.r/b,a.i/b);}
13 }a[M];
14 
15 void change(cp a[],int n){
16     for(int i=0,j=0;i1;i++){
17         if(i<j) swap(a[i],a[j]);
18         int k=n>>1;
19         while(j>=k) j-=k,k>>=1;
20         j+=k;
21     }
22 }
23 void FFT(cp a[],int n,int on){
24     change(a,n);
25     for(int h=2;h<=n;h<<=1){
26         cp wn=cp(cos(2*PI/h),sin(2*PI*on/h));
27         for(int j=0;jh){
28             cp w=cp(1,0);
29             for(int k=j;k>1);k++){
30                 cp u=a[k],t=a[k+(h>>1)]*w;
31                 a[k]=u+t; a[k+(h>>1)]=u-t;
32                 w=w*wn;
33             }
34         }
35     }
36     if(on==-1)
37         for(int i=0;in;
38 }
39 
40 int main(){
41     int n; scanf("%d",&n); double las=0;
42     int len=1; for(;len<=n*2;len<<=1);
43     a[0]=cp(1,0);
44     for(int hh=1;hh<=min(n,50);hh++){
45         //if(las<1e-5){printf("0\n"); continue;}
46         FFT(a,len,1);
47         for(int i=0;ia[i];
48         FFT(a,len,-1);
49         for(int i=n;i0,0);
50         for(int i=n;i;i--) a[i]=a[i-1]/i; a[0]=cp(1,0);
51         printf("%.10lf\n",a[n].r-las);
52         las=a[n].r;
53     }
54     for(int i=min(n,50)+1;i<=n;i++) printf("%.10lf\n",0);
55 }