【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 #include2 #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 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 h){ 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;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 a[i]; 48 FFT(a,len,-1); 49 for(int i=n;i 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 }