rt。
用的是 dp 预处理组合数。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 605;
const int mod = 1e9 + 7;
int n, m, l, Inv2, fac[N], Fac[N], C[N][N], dp[N][N];
inline int qpow(int base, int k) {
int res = 1;
while(k) {
if(k & 1) res = (res * base) % mod;
base = (base * base) % mod, k >>= 1;
}
return res % mod;
}
inline void init() {
Inv2 = qpow(2, mod - 2);
for(int i = 0 ; i < N ; ++ i) {
C[i][1] = i;
C[i][0] = C[i][i] = 1;
}
for(int i = 2 ; i < N ; ++ i)
for(int j = 1 ; j <= i ; ++ j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
fac[1] = 1;
for(int i = 2 ; i < N ; ++ i)
fac[i] = fac[i - 1] * i % mod, Fac[i] = (((Fac[i - 1] * i) % mod) * Inv2) % mod;
return ;
}
inline int calc(int n, int m, int l) {
memset(dp, 0, sizeof dp);
dp[0][0] = 1;
for(int i = 1 ; i <= n ; ++ i)
for(int j = 0 ; j <= m ; ++ j) {
int x;
for(int k = 1 ; k <= min({l, i, j + 1 /* 可以枚举到的临界值,连通块大小 */ }) ; ++ k) {// 链的情况
if(k == 1) x = fac[k];
else x = Fac[k] % mod;
dp[i][j] = (dp[i][j] + (dp[i - k][j - k + 1] * (C[n - i + k - 1 /* 钦定最小点 */ ][k - 1] * x) % mod) % mod) % mod;
// 若不钦定最小点的话可能会导致在这个转移中枚举了多个不同的连通块,导致 dp 出现后效性
}
for(int k = 2 ; k <= min({l, i, j}) ; ++ k) {
if(k == 2) x = fac[k - 1];
else x = Fac[k - 1] % mod;
dp[i][j] = (dp[i][j] + (dp[i - k][j - k] * (C[n - i + k - 1][k - 1] * x) % mod) % mod) % mod;
// 相当于 Q(n, m) / 2
}
}
return dp[n][m] % mod;
}
signed main() {
ios_base :: sync_with_stdio(NULL);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m >> l;
init();
cout << (calc(n, m, l) - calc(n, m, l - 1) + mod) % mod;
return 0;
}