1. 程式人生 > 其它 >luogu P3830 [SHOI2012]隨機樹

luogu P3830 [SHOI2012]隨機樹

題面傳送門
先看第一問,設\(f_i\)\(i\)次操作後的深度總和。
考慮選擇一個點讓它擴充套件,那麼得到的葉子節點的期望深度就是\(\frac{f_{i}}{i+1}+2\),就可以\(O(n)\)解決問題了。
再看第二問,設\(f_{i,j}\)\(i\)次操作,深度為\(j\)的概率。
列舉左邊子樹的運算元\(k\),再列舉左右兩邊的深度\(d1,d2\)。就可以得到轉移\(f_{i,\max(d1,d2)+1}=\frac{f_{j,d1}\times f_{i-j-1,d2}}{i}\)
字首和一下就可以做到\(O(n^3)\)
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (100+5)
#define M 5000000
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int op,n;
namespace Solve1{
	db dp[N+5];I void S1(){
		RI i,j;n--;dp[0]=0;for(i=1;i<=n;i++)dp[i]=dp[i-1]+2.0/(i+1);printf("%.6lf\n",dp[n]);
	}
}
namespace Solve2{
	db dp[N+5][N+5],Ns,Ans;I void S2(){
		n--;RI i,j,h;dp[0][0]=1;for(i=1;i<=n;i++){
			for(j=0;j<i;j++){
				Ns=0;for(h=0;h<=j;h++) Ns+=dp[i-j-1][h],dp[i][h+1]+=Ns*dp[j][h]/i;
				Ns=0;for(h=0;h<=i-j-1;h++) dp[i][h+1]+=Ns*dp[i-j-1][h]/i,Ns+=dp[j][h];
			}
		}for(i=1;i<=n;i++) Ans+=dp[n][i]*i;printf("%.6lf\n",Ans);
	}
}
int main(){
    freopen("1.in","r",stdin);
	scanf("%d%d",&op,&n);op^2?Solve1::S1():Solve2::S2();
}