题解 P2612 【[ZJOI2012]波浪】
Update On 2020.03.07: 修复了代码中的小问题,修改了部分细节,优化了阅读体验。感谢 Marser 指出代码错误。
考虑拆开绝对值计算贡献。对于
不难发现:在最终的排列中,
但是在这里大于
设
转移考虑数
-
一边与边界相邻,一边不与当前存在的连通块相邻。这会产生额外的一个连通块,并产生
-i 的价值,方案数为2-l ; -
一边与边界相邻,一边与当前存在的连通块相邻。这不会导致连通块数变化,产生
i 的价值,方案数为2-l ,要求j \neq 0 ; -
两边均与当前存在的连通块相邻。这会导致两个连通块合并从而连通块数减少
1 ,产生2i 的价值,方案数为j-1 ,要求j \geq 2 ; -
两边均不与当前存在的连通块相邻。这会产生额外的一个连通块,产生
-2i 的价值,方案数为j+1-l ; -
一边与当前存在的连通块相邻,另一边不与当前存在的连通块相邻。这不会产生额外的连通块,产生
0 的价值,方案数为2j-l ,要求j \neq 0 。
注意到
答案是
然后这鬼题还要数据类型分治……
还要注意在 DP 的过程中将
#include<bits/stdc++.h>
//This code is written by Itst
using namespace std;
namespace db{long double dp[2][110][10010][3];}
namespace flt{__float128 dp[2][110][10010][3];}
int N , M , K;
template < class T >
void out(T ans){
if(ans + 1e-14 >= 1){cout << "1." << string(K,'0') << endl; return;}
cout << "0.";
ans *= 10;
for(int i = 1 ; i <= K ; ++i){
cout << (int)(ans + (K == i) * 0.5);
ans = (ans - (int)ans) * 10;
}
}
template < class T >
inline void solve(T dp[][110][10010][3]){
T ans = 0;
dp[0][0][5000][0] = 1;
int now = 0;
for(int i = 1 ; i <= N ; ++i){
now ^= 1;
memset(dp[now] , 0 , sizeof(dp[now]));
for(int j = 0 ; j < i ; ++j)
for(int k = 0 ; k <= 10000 ; ++k)
for(int p = 0 ; p <= 2 ; ++p)
if(dp[now ^ 1][j][k][p]){
if(k - 2 * i >= 0)
dp[now][j + 1][k - 2 * i][p] += dp[now ^ 1][j][k][p] * (j + 1 - p) / i;
if(j)
dp[now][j][k][p] += dp[now ^ 1][j][k][p] * (j * 2 - p) / i;
if(j >= 2 && k + 2 * i <= 10000)
dp[now][j - 1][k + 2 * i][p] += dp[now ^ 1][j][k][p] * (j - 1) / i;
if(p != 2){
if(k - i >= 0)
dp[now][j + 1][k - i][p + 1] += dp[now ^ 1][j][k][p] * (2 - p) / i;
if(j && k + i <= 10000)
dp[now][j][k + i][p + 1] += dp[now ^ 1][j][k][p] * (2 - p) / i;
}
}
}
for(int i = M ; i <= 5000 ; ++i)
ans += dp[now][1][5000 + i][2];
out(ans);
}
int main(){
cin >> N >> M >> K;
if(K <= 8)
solve(db::dp);
else
solve(flt::dp);
return 0;
}