【JZOJ5342】【NOIP2017模拟9.2A组】赤壁情

67 篇文章 1 订阅
55 篇文章 0 订阅

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

我们设dp[i][j][k][0..2]表示当前放到第i个数,在排列中形成j个连续段,形成的波浪值为k,在开头结尾是否有段的方案数。显然m的最大值不会超过n^2,所以dp是3*n^4的。我们每次考虑将i+1放到哪里。有几种情况:
1、若开头或结尾有一个为空,i+1放到开头或结尾,并且i+1自成一段。
2、若开头或结尾有一个为空,i+1放到开头或结尾,i+1与邻近的一段合并。
3、i+1不放在开头或结尾,i+1自成一段。
4、i+1不放在开头或结尾,i+1接在某一段后(前)。
5、i+1将左右两段连在一起。
由于当前的排列存的是相对顺序,不用考虑一些明明靠在一起却又没有合并的段的情况。直接转移即可。由于最后一个点贼坑,所以在最后一个点要用__float128。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=1e2+5;
double f[2][maxn][maxn*maxn][3];
__float128 g[2][maxn][maxn*maxn][3];
ll n,m,q,p,i,t,j,k,l,x,y,z;
__float128 xx,ans;
void put(){
    if (xx==1)printf("1");
    else printf("0");
    if (q){
        printf(".");
        for (i=1;i<q;i++){
            xx=xx*10;
            t=xx;xx-=t;
            printf("%d",t);
        }
        xx=xx*10;
        t=xx;xx-=t;
        if (xx>=0.5) t++;
        printf("%d",t);
    }
}
int main(){
    freopen("river.in","r",stdin);freopen("river.out","w",stdout);
    scanf("%lld%lld%lld",&n,&m,&q);
    if (m>n*(n-1)/2){
        put();return 0;
    }
    if (q<30){
    z=n*n/2;f[0][0][z][0]=1;m+=z;
    for (i=1;i<=n;i++){
        p=1-p;
        memset(f[p],0,sizeof(f[p]));
        for (j=0;j<=i;j++)
            for (k=1;k<=n*n;k++)
                for (l=0;l<3;l++)
                    if (f[1-p][j][k][l]){
                        if (l<2){
                            f[p][j+1][k-i][l+1]+=f[1-p][j][k][l]*(1+(!l));
                            if (j)f[p][j][k+i][l+1]+=f[1-p][j][k][l]*(1+(!l));
                        }
                        f[p][j+1][k-2*i][l]+=f[1-p][j][k][l]*(j+1-l);
                        if (j) f[p][j][k][l]+=f[1-p][j][k][l]*(2*j-l);
                        if (j>1)f[p][j-1][k+2*i][l]+=f[1-p][j][k][l]*(j-1);
                    }
    }
    for (i=m;i<=n*n;i++)
        ans+=f[p][1][i][2];
    }else{
        z=n*n/2;g[0][0][z][0]=1;m+=z;
    for (i=1;i<=n;i++){
        p=1-p;
        memset(g[p],0,sizeof(g[p]));
        for (j=0;j<=i;j++)
            for (k=1;k<=n*n;k++)
                for (l=0;l<3;l++)
                    if (g[1-p][j][k][l]){
                        if (l<2){
                            g[p][j+1][k-i][l+1]+=g[1-p][j][k][l]*(1+(!l));
                            if (j)g[p][j][k+i][l+1]+=g[1-p][j][k][l]*(1+(!l));
                        }
                        g[p][j+1][k-2*i][l]+=g[1-p][j][k][l]*(j+1-l);
                        if (j) g[p][j][k][l]+=g[1-p][j][k][l]*(2*j-l);
                        if (j>1)g[p][j-1][k+2*i][l]+=g[1-p][j][k][l]*(j-1);
                    }
    }
    for (i=m;i<=n*n;i++)
        ans+=g[p][1][i][2];
    }
    xx=ans;
    for (i=2;i<=n;i++)
        xx/=i*1.0;
    put();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值