【ZJOI2012】波浪 &【NOIP2017模拟9.2A组】赤壁情

Description:

这里写图片描述

题解:

首先知道对于每一个n的排列,它对应一棵笛卡尔树,而对于每一棵编号为1~n的有n个节点的笛卡尔树,也一定对应一个排列。
这启发我们构这个排列相当于构一棵笛卡尔树,我们就从小往大加入节点。
对于一个新加的节点要么自成一棵子树,要么合并两棵子树,要么贴到一棵子树的前后,每个位置所产生的赤壁之意不同,注意序列的首和尾需要特殊判断。
状态大概是: fi,j,k,0/1,0/1 表示弄了前i个,形成了j个不包含首尾的段,赤壁之意的和是k,最后两位表示是否要包含首尾的段。
方程有11条,注意最后统计答案的时候一定是首尾合并了的并且j为0。
最后一个点需要用__float128,当然考场不能用,手动高精度。
Code:

#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++) 
#define abs(a) ((a) < 0 ? -(a) : (a))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define $ 5200 +
using namespace std;

const double wu = 0.00000001;
int n, m, k, o;
double f[2][101][10450][2][2]; //circle
__float128 f2[2][51][10450][2][2];

void write(double ans) {
    int p = ans;
    printf("%d.", p);
    while(k) {
        k --;
        ans *= 10;
        int p = ans; ans -= p;
        if(k == 0) {
            int q = ans * 10;
            if(q >= 5) p ++;
        }
        printf("%d", p);
    }
    printf("\n");
}

void Work1() {
    f[o][0][$ 0][0][0] = 1;
    fo(i, 0, n - 2) {
        o = !o; memset(f[o], 0, sizeof(f[o]));
        fo(j, 0, min(i, n - i)) {
            fo(k, $ max((-i * (i + 1)), -5000), $ min((i * (i + 1)), 5000))
                fo(u, 0, 1) fo(v, 0, 1) {
                    if(abs(f[!o][j][k][u][v]) > wu) {
                        int p = i + 1; double q = f[!o][j][k][u][v];
                        f[o][j + 1][k - 2 * p][u][v] += q;
                        if(!u) f[o][j][k - p][1][v] += q;
                        if(!v) f[o][j][k - p][u][1] += q;
                        //make new

                        if(!u && j > 0) f[o][j - 1][k + p][1][v] += q * j;
                        if(!v && j > 0) f[o][j - 1][k + p][u][1] += q * j;
                        //make new and kao

                        if(u) f[o][j][k][u][v] += q;
                        if(v) f[o][j][k][u][v] += q;
                        if(j > 0) f[o][j][k][u][v] += q * 2 * j;
                        //kao

                        if(j > 1) f[o][j - 1][k + 2 * p][u][v] += q * j * (j - 1);
                        if(u && j > 0) f[o][j - 1][k + 2 * p][u][v] += q * j;
                        if(v && j > 0) f[o][j - 1][k + 2 * p][u][v] += q * j;
                        //bin
                    }
                }
        }
    }
    o = !o; memset(f[o], 0, sizeof(f[o]));
    fo(k, $ max((-n * (n - 1)), -5000), $ min(n * (n - 1), 5000)) {
        f[o][0][k + n][1][1] += f[!o][0][k][0][1] + f[!o][0][k][1][0];
        f[o][0][k + 2 * n][1][1] += f[!o][0][k][1][1];
    }
    double ans = 0;
    fo(i, $ m, $ n * n / 2) ans += f[o][0][i][1][1];
    fo(i, 2, n) ans /= i;
    write(ans);
}

void write2(__float128 ans) {
    int p = ans;
    printf("%d.", p);
    while(k) {
        k --;
        ans *= 10;
        int p = ans; ans -= p;
        if(k == 0) {
            int q = ans * 10;
            if(q >= 5) p ++;
        }
        printf("%d", p);
    }
    printf("\n");
}

void Work2() {
    f2[o][0][$ 0][0][0] = 1;
    fo(i, 0, n - 2) {
        o = !o; memset(f2[o], 0, sizeof(f2[o]));
        fo(j, 0, min(i, n - i)) {
            fo(k, $ max((-i * (i + 1)), -5000), $ min((i * (i + 1)), 5000))
                fo(u, 0, 1) fo(v, 0, 1) {
                    if(abs(f2[!o][j][k][u][v]) > wu) {
                        int p = i + 1; __float128 q = f2[!o][j][k][u][v];
                        f2[o][j + 1][k - 2 * p][u][v] += q;
                        if(!u) f2[o][j][k - p][1][v] += q;
                        if(!v) f2[o][j][k - p][u][1] += q;
                        //make new

                        if(!u && j > 0) f2[o][j - 1][k + p][1][v] += q * j;
                        if(!v && j > 0) f2[o][j - 1][k + p][u][1] += q * j;
                        //make new and kao

                        if(u) f2[o][j][k][u][v] += q;
                        if(v) f2[o][j][k][u][v] += q;
                        if(j > 0) f2[o][j][k][u][v] += q * 2 * j;
                        //kao

                        if(j > 1) f2[o][j - 1][k + 2 * p][u][v] += q * j * (j - 1);
                        if(u && j > 0) f2[o][j - 1][k + 2 * p][u][v] += q * j;
                        if(v && j > 0) f2[o][j - 1][k + 2 * p][u][v] += q * j;
                        //bin
                    }
                }
        }
    }
    o = !o; memset(f2[o], 0, sizeof(f2[o]));
    fo(k, $ max((-n * (n - 1)), -5000), $ min(n * (n - 1), 5000)) {
        f2[o][0][k + n][1][1] += f2[!o][0][k][0][1] + f2[!o][0][k][1][0];
        f2[o][0][k + 2 * n][1][1] += f2[!o][0][k][1][1];
    }
    __float128 ans = 0;
    fo(i, $ m, $ n * n / 2) ans += f2[o][0][i][1][1];
    fo(i, 2, n) ans /= i;
    write2(ans);    
}

int main() {
    freopen("river.in", "r", stdin);    
    freopen("river.out", "w", stdout);
    scanf("%d %d %d", &n, &m, &k);
    if(k < 30) Work1(); else Work2();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值