Description
Input
但是现在我们不知道树的形态(只知道是二叉树),所以这里就需要概率帮忙了
我们考虑一棵
n
n
个节点的二叉树有多少种形态,左右子树分开考虑
对Catalan数熟悉的同学就可以看出,结点个数为n的不同形态二叉树个数为 Catalan(n) C a t a l a n ( n )
C(n+1)=4n+2n+2C(n) C ( n + 1 ) = 4 n + 2 n + 2 C ( n )
f[i][j]
f
[
i
]
[
j
]
表示一棵
i
i
节点的二叉树,异或值为的概率
还是需要利用左右子树分开考虑来做
f[n][(x+1)xor(y+1)]=∑n−1i=0(h[i]∗f[i][x])∗(h[n−1−i]∗f[n−1−i][y])
f
[
n
]
[
(
x
+
1
)
x
o
r
(
y
+
1
)
]
=
∑
i
=
0
n
−
1
(
h
[
i
]
∗
f
[
i
]
[
x
]
)
∗
(
h
[
n
−
1
−
i
]
∗
f
[
n
−
1
−
i
]
[
y
]
)
h[i]∗f[i][x]
h
[
i
]
∗
f
[
i
]
[
x
]
:
i
i
个结点的子树的形态个数为
最后:
f[n][i]/=h[n]
f
[
n
]
[
i
]
/
=
h
[
n
]
g[i][j] g [ i ] [ j ] 表示的是前 i i 棵子树异或值为的概率
g[i][j(xor)k]=g[i−1][j]∗f[a[i]][k] g [ i ] [ j ( x o r ) k ] = g [ i − 1 ] [ j ] ∗ f [ a [ i ] ] [ k ]
最后的答案就是 1−f[n][0] 1 − f [ n ] [ 0 ]
tip
卡特兰数太大了会不会炸掉啊
但其实用double做的话是没什么问题的= =
好像是因为浮点数什么神奇的运算?
注意只有一个子结点的情况:
for (int j=0;j<=127;j++) f[i][j+1]+=2.0*h[i-1]*f[i-1][j];
子树的大小为i-1,子数的SG值为j:
f[i−1][j]
f
[
i
−
1
]
[
j
]
大小为i-1的二叉树形态个数为
h[i−1]
h
[
i
−
1
]
,
∗2
∗
2
:到底是左子树还是右子树
那么根结点的SG值就等于
j+1
j
+
1
100个结点,最大的异或值为127
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=130;
const int Max=127; //最大异或值
int n,a[N];
double f[N][N],g[N][N],h[N];
int main()
{
int mx=0;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]),mx=max(mx,a[i]);
h[0]=1.0; h[1]=1.0; h[2]=2.0;
for (int i=3;i<=127;i++)
for (int j=0;j<i;j++)
h[i]+=h[j]*h[i-j-1]; //Catalan数递推式
f[1][0]=1.0; //一个结点的SG值为0
for (int i=2;i<=mx;i++) {
for (int j=0;j<=127;j++) f[i][j+1]+=2.0*h[i-1]*f[i-1][j]; //只有一个子结点
for (int j=1;j<i-1;j++) //左子树大小
for (int x=0;x<=127;x++)
for (int y=0;y<=127;y++) {
f[i][(x+1)^(y+1)]+=f[j][x]*h[j]*f[i-j-1][y]*h[i-j-1];
}
for (int j=0;j<=127;j++) f[i][j]/=h[i]; //总概率/方案数
}
for (int i=0;i<=127;i++) g[1][i]=f[a[1]][i];
for (int i=2;i<=n;i++)
for (int j=0;j<=127;j++)
for (int k=0;k<=127;k++)
g[i][j^k]+=g[i-1][j]*f[a[i]][k];
printf("%.6lf\n",1.0-g[n][0]);
return 0;
}