_Cheems
2024-11-20 15:56:58
厉害数数题,使我螺旋升天。然后是小蒟蒻看完题解后的感想,不得不说第一眼看是真的一头雾水。
先考虑给定初始高度如何求最终高度。题目说每次地震会使得相同高度的两个柱子(显然至多两个)中靠前的那个高度减一,不可能模拟整个过程,所以需要观察一些性质:
记一段后缀的高度集合为
这启示我们从后往前逐个确定最终高度,已确定
考虑 dp,记
考虑转移,记
当前柱子应消失:对
当前柱子应保留:需要根据对连续段
最终高度
最终高度
最后考虑
于是有转移:
复杂度
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ADD(a, b) a = (a + b) % mod
const int N = 1.2e3 + 5, mod = 1e9 + 7, inv2 = 5e8 + 4;
int n, a, g[N], f[N][N], jc[N], jcinv[N];
bool vis[N];
inline int qstp(int a, int k) {int res = 1; for(; k; a = a * a % mod, k >>= 1) if(k & 1) res = res * a % mod; return res;}
inline int C(int n, int m){
if(n < 0 || m < 0 || n < m) return 0;
return jc[n] * jcinv[m] % mod * jcinv[n - m] % mod;
}
signed main(){
jc[0] = jcinv[0] = 1;
for(int i = 1; i < N; ++i) jcinv[i] = qstp(jc[i] = jc[i - 1] * i % mod, mod - 2);
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%lld", &a), vis[a] = true;
g[0] = f[0][0] = 1;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
ADD(g[i], g[j - 1] * g[i - j] % mod * (i - j + 2) % mod * C(i - 1, j - 1) % mod);
int c0, c1 = 0;
for(int i = 1; i <= 2 * n; ++i){
c0 = i - 1 - c1;
if(!vis[2 * n - i + 1]){
for(int j = 0; j <= n; ++j)
if(j > c0) ADD(f[i][j], f[i - 1][j] * (j - c0) % mod);
}
else{
for(int j = 0; j <= n; ++j){
if(n > j + 1) ADD(f[i][j], f[i - 1][j]);
for(int k = j + 1; k <= n; ++k)
ADD(f[i][k], f[i - 1][j] * g[k - j - 1] % mod * C(c1 - j, k - j - 1) % mod * (k - j + 1) % mod);
}
}
c1 += vis[2 * n - i + 1];
}
cout << f[2 * n][n] * qstp(inv2, n) % mod;
return 0;
}