版权声明:蒟蒻的博文,dalao转载标明出处就好吖 https://blog.csdn.net/jokingcoder/article/details/89252535
题意
在
的全排列
中
问
的概率有多大,保留k位小数,
Solution
(这个
不是正文)
我们考虑一个数如果比左右两侧的数都大那么
,他的贡献为
同理,如果一个数比一侧的数大,比另一侧的数小,贡献为
;比两侧都小,贡献为
先从小到大排序,每次考虑插入时的状态,因为后插入的一定大,所以考虑和那些块相邻
表示前
个数,
个联通块,答案
为
,边界占了
个的方案数(暂时是可以这么讲的)
这一维可以仿佛滚掉
,加一个常数
即可
Attention
- dp数组其实都是些整数,但是数值很大所以可以直接开
long double
或者__float128
- 为什么我们进行dp的时候都不用判断这种情况是否能做到就直接进行转移呢?比如
if (l - i >= -D) dp[nxt][j + 1][l - i + D][p + 1] += nows * (2 - p);
为什么就一定能保证当前海浪放在边界的时候就一定能自成一段呢? - 这个很多题解也都说了,dp数组分两类,
时用
long double
,否则用__float128
,全用__float128
会T。。。
Tricks
这里才是正文(相信大佬们一定直接跳过了上面的dp部分)。
本文重点就是如何卡精度,方法有很多(比如还能猜数据开大小的?),但是局部动态开是会某些OJ上是会RE的。我采取了开两个namespace
的方法,用template
开不同的数组类型,这样的内存是两倍的,可能比较大(雾
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 110
#define D 4500
namespace db{long double dp[2][N][(D << 1) + 10][3];}
namespace flt{__float128 dp[2][N][(D << 1) + 10][3];}
int n, m, k;
template < class T >
inline void doit(T dp[][N][(D << 1) + 10][3]) {
int now = 1, nxt = 0;
dp[0][0][D][0] = 1;
for (int i = 1; i <= n; ++i) {
std :: swap(now, nxt);
memset(dp[nxt], 0, sizeof dp[nxt]);
for (int j = 0; j <= std :: min(i - 1, m); ++j) {
for (int l = -D; l <= D; ++l) {
for (int p = 0; p <= 2; ++p) {
T nows = dp[now][j][l + D][p];
if (!nows) continue;
if (p <= 2) {
if (j && l + i <= D) dp[nxt][j][l + i + D][p + 1] += nows * (2 - p);
if (l - i >= -D) dp[nxt][j + 1][l - i + D][p + 1] += nows * (2 - p);
}
if (l - (i << 1) >= -D) dp[nxt][j + 1][l - (i << 1) + D][p] += nows * (j + 1 - p);
if (j) dp[nxt][j][l + D][p] += nows * ((j << 1) - p);
if (j > 1 && l + (i << 1) <= D) dp[nxt][j - 1][l + (i << 1) + D][p] += nows * (j - 1);
}
}
}
}
T prt = 0;
for (int i = m; i <= D; ++i) {
prt += dp[nxt][1][i + D][2];
}
for (int i = 1; i <= n; ++i) {
prt /= i;
}
int tot = prt;
printf("%d.", tot);
while (k--) {
prt = (prt - tot * 1.0) * 10.0;
if (!k) prt = prt + 0.5;
tot = prt;
printf("%d", tot);
} printf("\n");
}
int main() {
scanf("%d%d%d", &n, &m, &k);
if (k <= 8) doit(db :: dp);
else doit(flt :: dp);
return 0;
}