bzoj 2688

卡特兰数+博弈论+dp

树上删边游戏的公式是一个节点的$sg$值为儿子节点$sg$值$+1$的异或和。

设$dp_{i,j}$表示$i$个节点的二叉树$sg$值为$j$的概率,二叉树个数为卡特兰数,转移即可。

整个游戏的$sg$值等于每棵二叉树的$sg$值异或和。

设$g_{i.j}$为前i棵二叉树$sg$值为$j$的方案数,转移即可,答案为$1-g_{n,0}$。

复杂度$O(n^4)$

#include 
using namespace std;
const int maxn = 155, m = 128;
int n;
int A[maxn];
double h[maxn], dp[maxn][maxn], f[maxn][maxn];
int main() {
    h[0] = 1;
    for(int i = 1; i < m; ++i)
        for(int j = 0; j < i; ++j)
            h[i] += h[j] * h[i - j - 1]; 
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &A[i]);
    dp[1][0] = 1;
    for(int i = 2; i <= n; ++i) {
        for(int j = 0; j < m; ++j)
            dp[i][j + 1] += 2 * dp[i - 1][j] * h[i - 1];
        for(int j = 1; j < i - 1; ++j)
            for(int a = 0; a < m; ++a)
                for(int b = 0; b < m; ++b)
                    dp[i][(a + 1) ^ (b + 1)] += dp[j][a] * dp[i - j - 1][b] * h[j] * h[i - j - 1];
        for(int j = 0; j < m; ++j) 
            dp[i][j] /= h[i];
    }
    f[0][0] = 1;
    for(int i = 1; i <= n; ++i) 
        for(int a = 0; a < m; ++a)
            for(int b = 0; b < m; ++b) {
                f[i][a ^ b] += f[i - 1][a] * dp[A[i]][b];
    //            printf("f[%d][%d] = %.3f dp[%d][%d] = %.3f\n", i - 1, a, f[i - 1][a], A[i], b, dp[A[i]][b]);
            }
    printf("%.6f\n", 1.0 - f[n][0]);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/2-321414133115/p/11356070.html

你可能感兴趣的:(bzoj 2688)