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