【ZJOI2012】波浪【NOIP2017】赤壁情

1 篇文章 0 订阅

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

先来一波套路:①从小到大插入能够去掉绝对值的影响②dp只需要处理相对位置就可以记录答案。
fi,j,k,l 表示当前做到i,段数(连续有数的被称作一段)为j,此时对答案贡献为k,左右两个边界有多少个被填上,满足这样的状态有多少种方案。
大致分为以下几种情况:
①当前插入的值自成一段没有贴边界,贡献为-2*i
②当前插入的值自成一段有贴边界,贡献为-i
③当前插入的值加入了别的段且不使两段合并,贡献为0
④当前插入的值加入了别的段且使两段合并,贡献为+2*i
在转移的时候注意一下边界问题。
因为精度要精确到30位,不想打小数高精度可以试试__float128黑科技,只是有点慢。

Code

#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<iostream>
#include<math.h>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
typedef long long ll;
typedef double db;
typedef __float128 fl;
const int N=102,S=5000;
int n,m,o,i,j,k,l,Z;
db f[2][102][S*2+5][3],tot;
fl g[2][102][S*2+5][3],ans,TOT;
void write(fl ans){
    int tot=ans;printf("%d.",tot);
    while(o--){
        ans=(ans-tot*1.0)*10.0;
        if(!o) ans=ans+0.5;
        tot=ans;printf("%d",tot);
    }printf("\n");
}
int main(){
    scanf("%d%d%d",&n,&m,&o);
    Z=0;f[0][0][S][0]=g[0][0][S][0]=1;
    if(o<=8){
    fo(i,1,n){
        Z^=1;memset(f[Z],0,sizeof(f[Z]));
        fo(j,0,i){
            fo(k,0,S*2){
                fo(l,0,2){
                    tot=f[Z^1][j][k][l];
                    if(!tot) continue;
                    if(l<2){
                        if(j) f[Z][j][k+i][l+1]+=tot*(2-l);
                        f[Z][j+1][k-i][l+1]+=tot*(2-l);
                    }
                    f[Z][j+1][k-2*i][l]+=tot*(j+1-l);
                    if(j) f[Z][j][k][l]+=tot*(2*j-l);
                    if(j>1) f[Z][j-1][k+2*i][l]+=tot*(j-1);
                }
            }
        }
    }}
    else{
    fo(i,1,n){
        Z^=1;memset(g[Z],0,sizeof(g[Z]));
        fo(j,0,i){
            fo(k,0,S*2){
                fo(l,0,2){
                    TOT=g[Z^1][j][k][l];
                    if(!TOT) continue;
                    if(l<2){
                        if(j) g[Z][j][k+i][l+1]+=TOT*(2-l);
                        g[Z][j+1][k-i][l+1]+=TOT*(2-l);
                    }
                    g[Z][j+1][k-2*i][l]+=TOT*(j+1-l);
                    if(j) g[Z][j][k][l]+=TOT*(2*j-l);
                    if(j>1) g[Z][j-1][k+2*i][l]+=TOT*(j-1);
                }
            }
        }
    }}
    ans=0;
    if(o<=8){fo(i,S+m,2*S) ans+=f[Z][1][i][2];}
    else {fo(i,S+m,2*S) ans+=g[Z][1][i][2];}
    fo(i,1,n) ans/=i*1.0;
    write(ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值