【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]=1jjk=1f[i1][k1]×f[i1][jk]

直接转移的概率显然是O(n3)的。

 

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

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

所以最终的复杂度就变成了O(Anlog n)

复制代码
 1 #include<bits/stdc++.h>
 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;i<n-1;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;j<n;j+=h){
28             cp w=cp(1,0);
29             for(int k=j;k<j+(h>>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;i<n;i++) a[i]=a[i]/n;
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;i<len;i++) a[i]=a[i]*a[i];
48         FFT(a,len,-1);
49         for(int i=n;i<len;i++) a[i]=cp(0,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 }
复制代码
posted @   AlphaInf  阅读(229)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示