- 定义一个序列的波动值为每相邻两项差的绝对值之和。
- 求一个长度为(n)的序列波动值大于等于(m)的概率,保留(d)位小数。
- (nle100,kle30)
动态规划
绝对值一看就非常麻烦,所以套路地想到从小到大枚举每一个数把它插入序列中,这样就能保证每次插入的数一定大于序列中已有的所有数,小于序列中没有的所有数。而大小关系一旦确定,绝对值也自然可以除去了。
考虑一个数(i)的贡献,分别考虑两侧,如果是边界则没有贡献,否则若(i)更大则产生(i)的贡献,若(i)更小则产生(-i)的贡献。
若把(i)插在序列中间,可以在插入时钦定它两侧的贡献。如果某侧是(i)说明这个空隙中不再插入数,如果某侧是(-i)说明这个空隙中必须插入数。因此我们可以记一下必须插入数的空隙个数(k),则把(i)插在序列中间的方案数其实也就是(k)。
若把(i)插在序列边上,两侧贡献的讨论和上面是一样的,但一个需要额外考虑的问题是它是否为最终的端点(如果是则边界侧贡献为(0),如果不是则边界侧必然需要插入数贡献只能为(-i))。因此我们还需要记录已确定的最终端点个数(x),则把(i)插在序列边上的方案数应该是(2-x)。
综上,设(f_{i,j,k,x})表示当前要插入(i),贡献总和为(j)(为了防止出现负数,给它加上(5000)),必须插入数的空隙个数为(k),已确定端点个数为(x)时的方案数。
初始状态只需考虑(1)是否为最终端点。
卡精度
本意应该是想让我们写高精度,懒了点直接用__float128
。
然而全部开__float128
大数据会T掉,因此需要特判(dle 8)的点开long double
。
代码:(O(n^4))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
#define V 5000
using namespace std;
int n,m,d;long double f1[2][2*V+5][N+5][3];__float128 f2[2][2*V+5][N+5][3];
Tp I void T(Ty x)//保留d位小数
{
if(!d) return (void)printf("%d
",round((double)x));printf("%d.",(int)x),x-=(int)x;//输出整数部分(特判d=0)
for(RI i=1;i^d;++i) printf("%d",(int)(x*=10)),x-=(int)x;printf("%d
",(int)round((double)(x*=10)));//直接输出前d-1位,四舍五入输出第d位
}
Tp I void Solve(Ty f[2][2*V+5][N+5][3])//动态规划
{
RI i,j,k,x;Ty v,nv;for(f[1][V-2][0][0]=1,f[1][V-1][0][1]=2,i=2;i<=n;++i)//初始化考虑1是否为最终端点
{
for(j=0;j<=2*V;++j) for(k=0;k^i;++k) for(x=0;x<=2;++x) f[i&1][j][k][x]=0;//清空
#define DP(_j,_k,_x) (_j>=0&&_j<=2*V&&(f[i&1][_j][_k][_x]+=nv))//转移,防数组越界
for(j=0;j<=2*V;++j) for(k=0;k^(i-1);++k) for(x=0;x<=2;++x) if(v=f[i&1^1][j][k][x]/i)
(nv=k*v)&&(DP(j-2*i,k+1,x),DP(j,k,x),DP(j,k,x),DP(j+2*i,k-1,x)),//插在序列中间,钦定两侧贡献
(nv=(2-x)*v)&&(DP(j,k,x),DP(j-2*i,k+1,x),DP(j+i,k,x+1),DP(j-i,k+1,x+1));//插在序列边上,讨论是否为最终端点,再钦定非边界侧贡献
}
Ty t=0;for(i=m;i<=V;++i) t+=f[n&1][V+i][0][2];T(t);//统计最终贡献和大于等于m的概率之和
}
int main()
{
if(scanf("%d%d%d",&n,&m,&d),n==1) return T(m?0:1),0;if(m>V) return T(0),0;//特判n=1和m过大
return d<=8?Solve(f1):Solve(f2),0;//数据分治
}