首先知道对于每一个n的排列,它对应一棵笛卡尔树,而对于每一棵编号为1~n的有n个节点的笛卡尔树,也一定对应一个排列。
这启发我们构这个排列相当于构一棵笛卡尔树,我们就从小往大加入节点。
对于一个新加的节点要么自成一棵子树,要么合并两棵子树,要么贴到一棵子树的前后,每个位置所产生的赤壁之意不同,注意序列的首和尾需要特殊判断。
状态大概是: fi,j,k,0/1,0/1 表示弄了前i个,形成了j个不包含首尾的段,赤壁之意的和是k,最后两位表示是否要包含首尾的段。
方程有11条,注意最后统计答案的时候一定是首尾合并了的并且j为0。
最后一个点需要用__float128,当然考场不能用,手动高精度。
Code:
#include
#include
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define abs(a) ((a) < 0 ? -(a) : (a))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define $ 5200 +
using namespace std;
const double wu = 0.00000001;
int n, m, k, o;
double f[2][101][10450][2][2]; //circle
__float128 f2[2][51][10450][2][2];
void write(double ans) {
int p = ans;
printf("%d.", p);
while(k) {
k --;
ans *= 10;
int p = ans; ans -= p;
if(k == 0) {
int q = ans * 10;
if(q >= 5) p ++;
}
printf("%d", p);
}
printf("\n");
}
void Work1() {
f[o][0][$ 0][0][0] = 1;
fo(i, 0, n - 2) {
o = !o; memset(f[o], 0, sizeof(f[o]));
fo(j, 0, min(i, n - i)) {
fo(k, $ max((-i * (i + 1)), -5000), $ min((i * (i + 1)), 5000))
fo(u, 0, 1) fo(v, 0, 1) {
if(abs(f[!o][j][k][u][v]) > wu) {
int p = i + 1; double q = f[!o][j][k][u][v];
f[o][j + 1][k - 2 * p][u][v] += q;
if(!u) f[o][j][k - p][1][v] += q;
if(!v) f[o][j][k - p][u][1] += q;
//make new
if(!u && j > 0) f[o][j - 1][k + p][1][v] += q * j;
if(!v && j > 0) f[o][j - 1][k + p][u][1] += q * j;
//make new and kao
if(u) f[o][j][k][u][v] += q;
if(v) f[o][j][k][u][v] += q;
if(j > 0) f[o][j][k][u][v] += q * 2 * j;
//kao
if(j > 1) f[o][j - 1][k + 2 * p][u][v] += q * j * (j - 1);
if(u && j > 0) f[o][j - 1][k + 2 * p][u][v] += q * j;
if(v && j > 0) f[o][j - 1][k + 2 * p][u][v] += q * j;
//bin
}
}
}
}
o = !o; memset(f[o], 0, sizeof(f[o]));
fo(k, $ max((-n * (n - 1)), -5000), $ min(n * (n - 1), 5000)) {
f[o][0][k + n][1][1] += f[!o][0][k][0][1] + f[!o][0][k][1][0];
f[o][0][k + 2 * n][1][1] += f[!o][0][k][1][1];
}
double ans = 0;
fo(i, $ m, $ n * n / 2) ans += f[o][0][i][1][1];
fo(i, 2, n) ans /= i;
write(ans);
}
void write2(__float128 ans) {
int p = ans;
printf("%d.", p);
while(k) {
k --;
ans *= 10;
int p = ans; ans -= p;
if(k == 0) {
int q = ans * 10;
if(q >= 5) p ++;
}
printf("%d", p);
}
printf("\n");
}
void Work2() {
f2[o][0][$ 0][0][0] = 1;
fo(i, 0, n - 2) {
o = !o; memset(f2[o], 0, sizeof(f2[o]));
fo(j, 0, min(i, n - i)) {
fo(k, $ max((-i * (i + 1)), -5000), $ min((i * (i + 1)), 5000))
fo(u, 0, 1) fo(v, 0, 1) {
if(abs(f2[!o][j][k][u][v]) > wu) {
int p = i + 1; __float128 q = f2[!o][j][k][u][v];
f2[o][j + 1][k - 2 * p][u][v] += q;
if(!u) f2[o][j][k - p][1][v] += q;
if(!v) f2[o][j][k - p][u][1] += q;
//make new
if(!u && j > 0) f2[o][j - 1][k + p][1][v] += q * j;
if(!v && j > 0) f2[o][j - 1][k + p][u][1] += q * j;
//make new and kao
if(u) f2[o][j][k][u][v] += q;
if(v) f2[o][j][k][u][v] += q;
if(j > 0) f2[o][j][k][u][v] += q * 2 * j;
//kao
if(j > 1) f2[o][j - 1][k + 2 * p][u][v] += q * j * (j - 1);
if(u && j > 0) f2[o][j - 1][k + 2 * p][u][v] += q * j;
if(v && j > 0) f2[o][j - 1][k + 2 * p][u][v] += q * j;
//bin
}
}
}
}
o = !o; memset(f2[o], 0, sizeof(f2[o]));
fo(k, $ max((-n * (n - 1)), -5000), $ min(n * (n - 1), 5000)) {
f2[o][0][k + n][1][1] += f2[!o][0][k][0][1] + f2[!o][0][k][1][0];
f2[o][0][k + 2 * n][1][1] += f2[!o][0][k][1][1];
}
__float128 ans = 0;
fo(i, $ m, $ n * n / 2) ans += f2[o][0][i][1][1];
fo(i, 2, n) ans /= i;
write2(ans);
}
int main() {
freopen("river.in", "r", stdin);
freopen("river.out", "w", stdout);
scanf("%d %d %d", &n, &m, &k);
if(k < 30) Work1(); else Work2();
}