[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);
}