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);
}