传送门:bzoj2817
题解
考虑拆去绝对值符号,而数列的相对大小波动如下:
发现只有转折点的数有贡献,可以套用两个套路:
- 从小到大插入去掉绝对值的影响
- 只需要记录拆分成了几段(相对位置)
设表示依次插入了,分成了段,差值之和为的方案数:
插入时有以下情况:
- 左右两边都有数,
- 只有一侧有数,
- 两侧都没有数,
段覆盖左右端点时情况比较特殊,所以还需要加一维,表示左右端点有个被占用了的方案数。
卡精度+卡内存
分情况用__float128/double
代码
做了许久口胡选手,代码能力急剧下降
#include<bits/stdc++.h>
using namespace std;
const int N=102,S=4500;
int n,m,K;
double f[2][N][S*2+5][3];
__float128 g[2][N][S*2+5][3];
template<class T>
inline void prit(T x)
{
int nw=x;printf("%d.",nw);
for(;K;--K){
x=(x-nw)*10.0;
if(K==1) x+=0.5;
nw=x;printf("%d",nw);
}
}
int main(){
int i,j,k,t,pr=0;
scanf("%d%d%d",&n,&m,&K);
if(K>8){
__float128 v,ans=0;g[0][0][S][0]=1;
for(i=1;i<=n;++i){
pr^=1;memset(g[pr],0,sizeof(g[pr]));
for(j=0;j<=i;++j)
for(k=0;k<=(S<<1);++k)
for(t=0;t<=2;++t) if((v=g[pr^1][j][k][t])!=0){
if(t<2){
if(j) g[pr][j][k+i][t+1]+=v*(2-t);
g[pr][j+1][k-i][t+1]+=v*(2-t);
}
if(j>1) g[pr][j-1][k+(i<<1)][t]+=v*(j-1);
g[pr][j+1][k-(i<<1)][t]+=v*(j+1-t);
g[pr][j][k][t]+=v*((j<<1)-t);
}
}
for(k=S+m;k<=(S<<1);++k)
ans+=g[pr][1][k][2];
for(i=2;i<=n;++i) ans/=i*1.0;
prit(ans);
}else{
double v,ans=0;f[0][0][S][0]=1;
for(i=1;i<=n;++i){
pr^=1;memset(f[pr],0,sizeof(f[pr]));
for(j=0;j<=i;++j)
for(k=0;k<=(S<<1);++k)
for(t=0;t<=2;++t) if((v=f[pr^1][j][k][t])!=0){
if(t<2){
if(j) f[pr][j][k+i][t+1]+=v*(2-t);
f[pr][j+1][k-i][t+1]+=v*(2-t);
}
if(j>1) f[pr][j-1][k+(i<<1)][t]+=v*(j-1);
f[pr][j+1][k-(i<<1)][t]+=v*(j+1-t);
if(j) f[pr][j][k][t]+=v*((j<<1)-t);
}
}
for(k=S+m;k<=(S<<1);++k)
ans+=f[pr][1][k][2];
for(i=2;i<=n;++i) ans/=i*1.0;
prit(ans);
}
return 0;
}