zoukankan      html  css  js  c++  java
  • 【BZOJ 2688】 2688: Green Hackenbush (概率DP+博弈-树上删边)

    2688: Green Hackenbush

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 42  Solved: 16

    Description

      有一个古老的游戏叫做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
    ",ans);
    45     return 0;
    46 }
    View Code

    2017-04-21 18:32:45

  • 相关阅读:
    c#隐藏和重写基类方法的区别
    c#事件(续委托之后)
    c# 中接口和抽象类的区别
    call 和apply 的一个记录
    string 和 stringbuilder 的区别
    传值和传引用
    值类型和引用类型
    标签选择器与字体连写
    input表单案例
    codeforces 676B B. Pyramid of Glasses(模拟)
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/6744963.html
Copyright © 2011-2022 走看看