d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示当前到第i个点用了j个边最大联通块是否为L的方案数。
满足题目的联通快必然是一个链树或者一个环。分树和环来进行转移即可。
注意这里大小为n的环有
(
n
−
1
)
!
/
2
(n-1)!/2
(n−1)!/2种,大小为n的一条链树有
n
!
/
2
n!/2
n!/2种。
注意一下特殊的case,例如大小为1 2的时候。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<<x<<endl;
int n,m,l;
const int mod=1e9+7;
const int inv2=(mod+1)/2;
int add(int x,int y){
x+=y;
if(x>=mod)x-=mod;
return x;
}
int mul(int x,int y){
return 1ll*x*y%mod;
}
int ksm(int x,int y){
int z=1;
while(y){
if(y&1)z=1ll*z*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return z;
}
int fac[N],inv[N];
void P(){
fac[0]=1;
for(int i=1;i<=300;i++)fac[i]=mul(fac[i-1],i);
inv[300]=ksm(fac[300],mod-2);
for(int i=299;i>=0;i--)inv[i]=mul(inv[i+1],i+1);
}
int C(int x,int y){
if(x<y)return 0;
return mul(fac[x],mul(inv[y],inv[x-y]));
}
int dp[333][333][2];
int main() {
ios::sync_with_stdio(false);
cin>>n>>m>>l;P();
dp[0][0][0]=1;
for(int i=1;i<=n;i++){
for(int j=i-1;j>=0&&i-j<=l;j--){
int now=i-j;
for(int k=m;k>=now-1&&k>=0;k--){
for(int sta=0;sta<2;sta++) {
int res=(sta|(now==l));
if (k - now >= 0&&now>1){//cycle
if(now>2)dp[i][k][res] = add(dp[i][k][res], mul(dp[j][k - now][sta], mul(C(n - j-1, now-1),mul(fac[now-1],inv2))));
else dp[i][k][res] = add(dp[i][k][res], mul(dp[j][k - now][sta], mul(C(n - j-1, now-1),1)));
}
if(now>1)dp[i][k][res] = add(dp[i][k][res], mul(dp[j][k - now + 1][sta], mul(C(n - j-1, now-1),mul(fac[now],inv2))));
else dp[i][k][res] = add(dp[i][k][res], mul(dp[j][k - now + 1][sta], mul(C(n - j-1, now-1),1)));
}
}
}
}
cout<<dp[n][m][1]<<'\n';
return 0;
}