[BZOJ2688]Green Hackenbush 题意: 有$n$棵随机的二叉树,每棵只知道大小为$a_i$
博弈:每次选取一个子树删掉,只剩根不能操作,求先手获胜概率
考虑这个博弈,求出一棵树的$\text{SG}$值
显然有:
1.只有一个点的树的$\text{SG}$值为0
2.多个树组合的问题为$\text{SG}$值异或
暴力$dp$,对于树$T$求答案,设$T$所有可行的后继状态集合为$N(T)$,则得到$\text{SG}$值的表达式为
$\text{SG}(T)=\text{mex}_{R\in N(T)}\lbrace\text{SG(R)}\rbrace $
直接求解复杂度过高,考虑归纳性质
性质:
1.一棵根节点只有一个儿子的树,其$\text{SG}$值为儿子的$\text{SG}$值+1
考虑归纳证明:
设子树为$T$,令$T+u$表示$T$子树上面接上自己作为根,问题变为求证$\text{SG}(T+u)=\text{SG}(T)+1$
设已经归纳证明所有$T$的子联通块成立
我们要求$\text{SG}(T+u)$
$\text{SG}(T+u)=\text{mex} \{\text{SG}(u),\forall _{R\in N(T)}\text{SG}(R+u)\}$
由归纳的性质有
$\forall _{R\subsetneq T}\text{SG}(R+T)=\text{SG}(R)+1$
又因为$\text{SG}(u)=0$,看做把所有儿子的情况平移了1,0的位置由自己占据,因而上式成立
2.多叉树的问题可以归纳为 根分别接上每个儿子得到的树 的问题的组合
因为儿子之间实际互不干扰,比较容易理解
由此得到,一棵树的$\text{SG}$值为其所有儿子的$\text{SG}$值+1的异或和
令$dp_{n,i}$为一棵$n$个节点的二叉树$\text{SG}$值为$i$的概率,为了便于转移,设空树的$\text{SG}$值为-1
考虑直接枚举两棵子树的大小和$\text{SG}$值
考虑对于$n$个节点的二叉树,设其左儿子为$i$时的总概率为$F_i$
得到的$\text{dp}$转移是
$dp_{n,(a+1)\oplus (b+1)}\leftarrow {dp_{i,a}\cdot dp_{n-i-1,b}\cdot F_i}$
我们知道$n$个节点的二叉树方案数为$Catalan(n)=\frac{(2n)!} {n!(n+1)!}$
由此得到$\begin{aligned} F_i=\frac{Catalan(i)Catalan(n-i-1)} {Catalan(n)}\end{aligned} $
此题范围可以直接带入$Catalan(i)$求解,但是依然要提一下递推的做法(似乎精度更有保障?)
$\begin{aligned} F_i=\frac{\frac{(2i)!} {i!(i+1)!}\cdot \frac{(2n-i-2)!} {(n-i-1)!(n-i)!} } {\frac{(2n)} {n!(n+1)!} }\end{aligned} $
递推求解$F_i$,每次$i$改变一阶乘只会改变1或者2,因此由$F_{i-1}$得到$F_i$的递推式为
$F_i=\left\{ \begin{aligned}\frac{n(n+1)} {2n(2n-1)}&& i=0\\ F_{i-1}\cdot \frac{2i(2i-1)} {(i+1)i}\frac{(n-i+1)(n-i)} {2(n-i)(2n-2i-1)} && i\in[1,n-1]\end{aligned}\right.$
化简之后应该是
$F_i=\left\{ \begin{aligned}\frac{(n+1)} {2(2n-1)}&& i=0\\ F_{i-1}\cdot \frac{(2i-1)} {(i+1)}\frac{(n-i+1)} {(2n-2i-1)} && i\in[1,n-1]\end{aligned}\right.$
至此得到一个朴素的$O(n^4)$预处理,由于是异或,可以用$\text{FWT}_{\oplus}$求解,复杂度为$O(n^3)$
对于输入的每棵树,类似背包地叠加概率即可,复杂度为$O(n^3)$
以下是朴素dp代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <bits/stdc++.h> using namespace std;typedef double db;#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i) #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i) char IO;template <class T =int > T rd () { T s=0 ; int f=0 ; while (!isdigit (IO=getchar ())) if (IO=='-' ) f=1 ; do s=(s<<1 )+(s<<3 )+(IO^'0' ); while (isdigit (IO=getchar ())); return f?-s:s; } const int N=128 ;int n;db dp[N][N]; void FWT (db *a,int f) { for (int i=1 ;i<N;i<<=1 ){ for (int l=0 ;l<n;l+=i*2 ) { for (int j=l;j<l+i;j++){ db t=a[j+i]; a[j+i]=a[j]-t; a[j]+=t; } } } if (f==-1 ) rep (i,0 ,N-1 ) a[i]/=N; } db F[N],G[N]; int main () { dp[0 ][0 ]=1 ,dp[1 ][0 ]=1 ; rep (i,2 ,100 ) { F[0 ]=1.0 /(2 *i)/(2 *i-1 )*(i+1 )*i; rep (j,1 ,i-1 ) { F[j]=F[j-1 ] * (2 *j)*(2 *j-1 )/(j+1 )/j * 1.0 /(2 *(i-j))/(2 *(i-j)-1 )*(i-j+1 )*(i-j); } rep (a,0 ,i-1 ) rep (h1,0 ,N-1 ) if (dp[a][h1]>0 ) { rep (h2,0 ,N-1 ) if (dp[i-a-1 ][h2]) { int nxt=0 ; if (a>0 ) nxt^=h1+1 ; if (i-1 -a>0 ) nxt^=h2+1 ; dp[i][nxt]+=dp[a][h1]*dp[i-a-1 ][h2]*F[a]; } } } n=rd (); rep (i,0 ,N-1 ) F[i]=0 ; F[0 ]=1 ; rep (i,1 ,n) { int x=rd (); rep (j,0 ,N-1 ) G[j]=0 ; rep (j,0 ,N-1 ) if (F[j]) rep (k,0 ,N-1 ) G[j^k]+=F[j]*dp[x][k]; rep (j,0 ,N-1 ) F[j]=G[j]; } db ans=0 ; rep (i,1 ,N-1 ) ans+=F[i]; printf ("%.6lf\n" ,ans); }
以下是FWT优化代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <bits/stdc++.h> using namespace std;typedef double db;#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i) #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i) char IO;template <class T =int > T rd () { T s=0 ; int f=0 ; while (!isdigit (IO=getchar ())) if (IO=='-' ) f=1 ; do s=(s<<1 )+(s<<3 )+(IO^'0' ); while (isdigit (IO=getchar ())); return f?-s:s; } const int N=128 ;int n;db dp[N][N],T[N][N]; void FWT (db *a,int f) { for (int i=1 ;i<N;i<<=1 ){ for (int l=0 ;l<N;l+=i*2 ) { for (int j=l;j<l+i;j++){ db t=a[j+i]; a[j+i]=a[j]-t; a[j]+=t; } } } if (f==-1 ) rep (i,0 ,N-1 ) a[i]/=N; } db F[N],G[N]; int main () { dp[0 ][0 ]=1 ,dp[1 ][0 ]=1 ; T[0 ][0 ]=1 ,T[1 ][1 ]=1 ; FWT (T[0 ],1 ),FWT (T[1 ],1 ); rep (i,2 ,100 ) { F[0 ]=1.0 /(2 *i)/(2 *i-1 )*(i+1 )*i; rep (j,1 ,i-1 ) { F[j]=F[j-1 ] * (2 *j)*(2 *j-1 )/(j+1 )/j * 1.0 /(2 *(i-j))/(2 *(i-j)-1 )*(i-j+1 )*(i-j); } rep (j,0 ,i-1 ) rep (k,0 ,N-1 ) dp[i][k]+=T[j][k]*T[i-j-1 ][k]*F[j]; FWT (dp[i],-1 ); rep (j,0 ,N-2 ) T[i][j+1 ]=dp[i][j]; FWT (T[i],1 ); } n=rd (); rep (i,0 ,N-1 ) F[i]=0 ; F[0 ]=1 ; rep (i,1 ,n) { int x=rd (); rep (j,0 ,N-1 ) G[j]=0 ; rep (j,0 ,N-1 ) if (F[j]) rep (k,0 ,N-1 ) G[j^k]+=F[j]*dp[x][k]; rep (j,0 ,N-1 ) F[j]=G[j]; } db ans=0 ; rep (i,1 ,N-1 ) ans+=F[i]; printf ("%.6lf\n" ,ans); }