【BZOJ 2688】 2688: Green Hackenbush (概率DP+博弈-树上删边)
2688: Green Hackenbush
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 42 Solved: 16Description
有一个古老的游戏叫做Green Hackenbush,游戏是这样进行的:两个人轮流在一棵树上删边,每次删边后不与根联通的子树直接被ignore,不能删边的游戏者输。Alice和Bob也在玩这个游戏,不过他们面对的是n棵树,第i棵树是含有a[i]个节点的二叉树。先手的Alice想知道自己有多大的概率获胜(假设我们的Alice和Bob同学都是无限聪明的)。Input
第一行一个数n。接下来每行一个数a[i]。Output
一个保留6位小数的实数ans。Sample Input
1
2
Sample Output
1.000000
HINT
对于100%的数据,n<=100,a[i]<=100
【分析】
想到了,但是以为过不了的复杂度。。
树上删边游戏有一个结论就是:树的sg值等与子树的sg值+1的乘积。
证明具体看:http://www.cnblogs.com/Konjakmoyu/p/5412444.html
f[i][j]表示规模为i的子树,其sg为j的概率。
因为是二叉树,枚举一下子树就好了。概率用方案数/总方案数 求,这个方案数呢是卡特兰数啊显然,然后dalao说用double就好了?
【然后四重循环的复杂度也很迷人。。
然后把多棵树合起来就是g[i][j]表示前i棵数,sg异或和为j的概率。
最后把sg不为0的概率加起来就是答案。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Maxn 210 8 9 double f[Maxn][Maxn],sm[Maxn]; 10 double g[Maxn][Maxn]; 11 int a[Maxn]; 12 13 int main() 14 { 15 int n,mx=0,M; 16 scanf("%d",&n); 17 for(int i=1;i<=n;i++) {scanf("%d",&a[i]);mx=max(mx,a[i]);} 18 M=1;while(M<=mx) M<<=1;M--; 19 f[1][0]=1.0;sm[1]=1; 20 for(int i=2;i<=mx;i++) 21 { 22 sm[i]=sm[i-1]*2; 23 for(int j=0;j<=M;j++) f[i][j]=f[i-1][j-1]*2*sm[i-1]; 24 for(int j=0;j<i;j++) 25 { 26 sm[i]+=sm[j]*sm[i-j-1]; 27 for(int k=0;k<=M;k++) 28 for(int l=0;l<=M;l++) 29 { 30 f[i][(k+1)^(l+1)]+=f[j][k]*f[i-j-1][l]*sm[j]*sm[i-j-1]; 31 } 32 } 33 for(int j=0;j<=M;j++) f[i][j]/=sm[i]; 34 } 35 g[0][0]=1.0; 36 for(int i=1;i<=n;i++) 37 { 38 for(int j=0;j<=M;j++) 39 for(int k=0;k<=M;k++) 40 g[i][j]+=g[i-1][k]*f[a[i]][j^k]; 41 } 42 double ans=0; 43 for(int j=1;j<=M;j++) ans+=g[n][j]; 44 printf("%.6lf\n",ans); 45 return 0; 46 }
2017-04-21 18:32:45